Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir 使用bind_quoted语句获得意外结果_Elixir - Fatal编程技术网

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