Elixir 使用bind_quoted语句获得意外结果
我试图理解宏中的Elixir 使用bind_quoted语句获得意外结果,elixir,Elixir,我试图理解宏中的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
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
我知道bind\u quoted
只执行一次表达式。我的问题是,有人能解释一下为什么我得到了意想不到的结果吗 Second
:ok
是表达式的结果,由REPL打印,而不是代码<代码>IO.puts返回:ok
。
正如您可以签入宏。展开-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
)
)
只需复制粘贴已在以下位置给出的答案:
这并不奇怪,也没有文档记录,但如果您使用bind_quote
选项将其扩展为quote/2
,它将扩展为如下内容:
# your code
quote bind_quoted: [foo: foo] do
IO.inspect foo
end
因此,在您的示例中注入的内容是:
quote do
expression = unquote(expression)
IO.puts "============="
IO.inspect expression
IO.puts "============="
expression
end
以这种方式来看,应该清楚地说明为什么您得到的输出是这样的
即使使用bind\u quoted
的打字量要少得多,我也强烈反对使用它:
你在上面举了一个例子,你对执行的顺序失去了控制
您可能会忘记哪些项目没有报价,哪些项目来自“内部”
您失去了手动取消引用其他绑定的能力,这是一个全有或全无的决定
编辑
在我忘记它之前…日志应该没有副作用(除了日志;))。因此,要么从“基本”函数记录您即将进行远程API调用,要么从远程API函数记录您正在进行调用。但是不要向记录器传递记录器确实执行的函数…由于宏中的代码注入,可能存在有害的上下文更改 请不要跨越多个媒体,如果你真的必须,选择一个中心位置,让其他帖子链接到它@zero_编码我是否可以建议,如果您试图学习如何使用宏,投资Chris McCord的“元编程长生不老药”将是明智之举?似乎这可以帮助你在这些问题上帮助自己。当然,这只是一个建议。这不是他想知道的第二个问题。事实上,输出的前两行与他的预期相反。但是我花了一个diff
-运行才能实现;)哦是的,我的问题完全错了。不确定,我是否应该删除此答案?。事实上,您的答案确实显示了一个事实,即给定宏的参数在给定:do
块的实际内容之前是不带引号的。也许你可以改写你的引导句,使之成为问题的有效答案?首先,非常感谢你的回答。我忘了IO.puts return:ok,它在宏调用之前执行。我太笨了。它不是在宏调用之前执行的,它是在每个unquote
上执行的,并且使用bind\u quote
你很早就unquote
了。
# 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