我一直在通過宏(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
和結果的通話清單循環。它是否正確?任何指向我可能調試/診斷的指針也將被讚賞,以及進一步閱讀的鏈接。
謝謝你提出的解決問題的方法實際上是我如何解決這個問題,這讓我對自己開始明白髮生了什麼充滿信心! – Taufiq