也许我误解了bind_quoted的使用但是采用这个简单的while循环宏:
defmodule Loop do
defmacro while(expression, do: block) do
quote bind_quoted: [expression: expression, block: block] do
for _ <- Stream.cycle([:ok]) do
if(expression) do
block
end
end
end
end
end
用法:
Interactive Elixir (1.8.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "loop.ex"
[Loop]
iex(2)> import Loop
iex(3)> while 1 == 1 do
...(3)> IO.puts "hi"
...(3)> end
hi
我希望无限循环“hi”,但只能获得一次迭代。如果我删除bind_quoted和简单的unquote()每个参数,它按预期工作。有任何想法吗?
谢谢
当你使用bind_quoted时,它会评估你传入的部分。所以IO.puts "hi"
在[expression: expression, block: block]
中评估,而不是在你的if
块中。你只是在那里注射:ok
。
作为一个例子,让我们定义一些除了评估传入内容之外什么都不做的宏:
defmodule AB do
defmacro a(block) do
quote bind_quoted: [block: block] do
block
block
:ok
end
end
defmacro b(block) do
quote do
unquote(block)
unquote(block)
:ok
end
end
end
忽略第一个警告,让我们用包含IO.inspect/1
的块调用每个警告,看看会发生什么:
iex> require AB
iex> AB.a(IO.inspect(1 + 1))
... (some warnings)
2
:ok
iex> AB.b(IO.inspect(1 + 1))
2
2
:ok
在宏a/1
中,sum + inspect在引用之外发生,所以它只被称为一次。在宏b/1
中,总和+检查发生在我们不引用block
的任何地方。