这有效:
1: kd> !process fffffa8014c25170 2
PROCESS fffffa8014c25170
SessionId: 1 Cid: 0ad4 Peb: 7fffffdf000 ParentCid: 07b8
DirBase: 2b451000 ObjectTable: fffff8a002e61620 HandleCount: 12.
Image: exe-once.exe
THREAD fffffa8013958b60 Cid 0ad4.0724 Teb: 000007fffffdd000 Win32Thread: 0000000000000000 WAIT: (UserRequest) UserMode Non-Alertable
fffffa8013aa1060 Thread
THREAD fffffa8013aa1060 Cid 0ad4.01e8 Teb: 000007fffffdb000 Win32Thread: 0000000000000000 WAIT: (DelayExecution) KernelMode Non-Alertable
fffffa8013aa1420 Semaphore Limit 0x1
但这不是:
defmacro returning(expr, do: block) do
quote do
case unquote(expr), do: unquote(block)
end
end
为什么?
答案 0 :(得分:3)
关于背景。以下代码等同于使用bind_quoted
的代码:
defmacro returning(expr, do: block) do
quote do
expr = unquote(expr)
block = unquote(block)
case expr, do: block
end
end
所以,我们说我们写的是:
returning(true) do
true -> :ok
false -> :not_ok
end
这会生成以下AST:
{:__block__, [],
[{:=, [], [{:expr, [], Test}, true]},
{:=, [], [{:block, [], Test}, [{:->, [line: 46], [[true], :ok]}, {:->, [line: 47], [[false], :not_ok]}]]},
{:case, [], [{:expr, [], Test}, [do: {:block, [], Test}]]}]}
这个元组的工作原理如下:
{<name of the function>, <context>, <arguments>}
{:=, [], [{:expr, [], Test}, true]}
,相当于expr = true
。这不会引起任何错误。block = <something>
。 <something>
在此上下文中,Elixir将true -> :ok
和false -> :not_ok
子句解释为函数->([true], :ok)
和->([false], :not_ok)
,尝试评估它们并且找不到它们。另一个宏,那个没有失败的宏,你会得到以下AST相同的代码:
{:case, [],
[true,
[do: [{:->, [line: 42], [[true], :ok]},
{:->, [line: 43], [[false], :not_ok]}]]]}
在案例的上下文中,:->
被解释为case
语句的子句而不是函数。在case
声明之前没有变量绑定,所以对于编译器来说,一切都是找到的。
这就是bind_quoted
在这种情况下不起作用的原因。仅仅是因为不了解背景。
我希望这能回答你的问题。
PS :
我使用以下代码检查宏的AST差异:
defmodule Test do
defmacro good_returning(expr, do: block) do
ast = quote do
case unquote(expr), do: unquote(block)
end
IO.inspect ast
:ok
end
defmacro bad_returning(expr, do: block) do
ast = quote bind_quoted: [expr: expr, block: block] do
case expr, do: block
end
IO.inspect ast
:ok
end
end
import Test
good_returning(true) do
true -> :ok
false -> :not_ok
end
bad_returning(true) do
true -> :ok
false -> :not_ok
end