我正在尝试理解宏中的bind_quoted
并拥有以下宏模块:
defmodule Debugger do
defmacro log(expression) do
if Application.get_env(:debugger, :log_level) == :debug do
quote bind_quoted: [expression: expression] do
IO.puts "============="
IO.inspect expression
IO.puts "============="
expression
end
else
expression
end
end
end
然后在shell中,我按照模块
进行操作iex(1)> import_file "debugger_fixed.exs"
{:module, Debugger,
<<70, 79, 82, 49, 0, 0, 6, 224, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 158, 131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115, 95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>,
{:log, 1}}
iex(2)> require Debugger
nil
iex(3)> Application.put_env(:debugger, :log_level, :debug)
:ok
iex(4)> remote_api_call = fn -> IO.puts("calling remote API...") end
iex(7)> Debugger.log(remote_api_call.())
作为最后一行的结果,我有
calling remote API...
=============
:ok
=============
:ok
但我希望
=============
calling remote API...
:ok
=============
:ok
我知道bind_quoted
只执行一次表达式
我的问题是,有人可以解释为什么我有意想不到的结果?
答案 0 :(得分:1)
第二个:ok
是您的表达式的结果,由REPL打印,而不是您的代码。 IO.puts
返回:ok
。
您可以签入Macro.expand
- ed代码,表达式只需评估一次。
iex(9)> quote(do: Debugger.log(remote_api_call.())) |> Macro.expand(__ENV__) |>
...(9)> Macro.to_string |> IO.puts
(
expression = remote_api_call.()
(
IO.puts("=============")
IO.inspect(expression)
IO.puts("=============")
expression
)
)
答案 1 :(得分:1)
只需复制粘贴我在Elixirforum – Get unexpected result with bind_quoted statement给出的答案:
这并不奇怪,也没有记录,但如果你对bind_quoted
使用quote/2
选项,它会扩展为类似的内容:
# your code
quote bind_quoted: [foo: foo] do
IO.inspect foo
end
# what the compiler makes out of it in a first expansion pass of many!
quote do
foo = unquote(foo)
IO.inspect foo
end
所以在你的例子中注入的是:
quote do
expression = unquote(expression)
IO.puts "============="
IO.inspect expression
IO.puts "============="
expression
end
以这种方式看待,应该明白为什么你可以得到你的输出。
即使使用bind_quoted
打字的次数要少得多,我强烈反对使用它:
修改强>
在我忘记它之前......记录应该没有副作用(除了记录;))。因此,要么从您的“基础”函数中记录您即将进行远程API调用,要么从您现在正在进行调用的远程API函数中记录。但是不要将一个函数传递给记录器执行它的记录器......由于宏中的代码注入可能会对上下文产生有害的变化!