【发布时间】:2016-10-10 12:55:41
【问题描述】:
我一直在进行宏练习(Dave Thomas 的出色Programming Elixir 1.2,第 21 章),我对正在发生的事情的理解有点困难。我有两个模块,Tracer 和Test,其中Tracer 重新定义了def 宏以插入调用和响应日志记录,如下所示:
defmodule Tracer do
def dump_args(args) do
args |> Enum.map(&inspect/1) |> Enum.join(",")
end
def dump_defn(name, args) do
"#{name}(#{dump_args(args)})"
end
defmacro def({name, _, args} = definition, do: content) do
IO.puts("Definition: #{inspect definition}")
IO.puts("Content: #{inspect content}")
quote do
Kernel.def unquote(definition) do
IO.puts("==> call: #{Tracer.dump_defn(unquote(name), unquote(args))}")
result = unquote(content)
IO.puts("<== resp: #{inspect result}")
result
end
end
end
end
和Test 使用这个宏来演示它的用法:
defmodule Test do
import Kernel, except: [def: 2]
import Tracer, only: [def: 2]
def puts_sum_three(a, b, c), do: IO.inspect(a+b+c)
def add_list(list), do: Enum.reduce(list, 0, &(&1 + &2))
def neg(a) when a < 0, do: -a
end
执行此操作时,前两个函数按预期工作:
iex(3)> Test.puts_sum_three(1,2,3)
==> call: puts_sum_three(1,2,3)
6
<== resp: 6
6
iex(4)> Test.add_list([1,2,3,4])
==> call: add_list([1, 2, 3, 4])
<== resp: 10
10
但是,第三个函数似乎挂起 - 具体来说,它挂在 unquote(args):
iex(5)> Test.neg(-1)
所以我的问题是,是什么导致调用 neg 挂起?虽然我认为我有一个想法,但我想确认(或反驳)我对它为什么这样做的理解。
第三个函数中的when 子句更改了传递给def 宏的带引号的表达式的表示形式,我在修改宏来处理这个问题时没有任何问题。为neg 传递的definition 和content 是:
Definition: {:when, [line: 7], [{:neg, [line: 7], [{:a, [line: 7], nil}]}, {:<, [line: 7], [{:a, [line: 7], nil}, 0]}]}
Content: {:-, [line: 7], [{:a, [line: 7], nil}]}
当我们到达neg 中的unquote(args) 时,它正在尝试评估args 表达式,我认为这是一个包含对neg 的调用的列表,并导致无限递归循环。这个对吗?任何有关我如何调试/诊断此问题的指针以及指向进一步阅读的链接也将不胜感激。
【问题讨论】:
标签: elixir