2016-10-10 6 views
2

(デイブ・トーマスの優れたProgramming Elixir 1.2、チャプター21)をハングし、私は何が起こっているの私の理解では、バンプのビットをヒットしました。実行時にはUnquoting引数は、私はマクロの行使により取り組んできた関数呼び出し

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 

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は、その使用を示すために、このマクロを使用しています。私はTracerは次のように呼び出しと応答のロギングを挿入するマクロdefを再定義する二つのモジュール、TracerTestを、持っていますこれは、最初の2つの関数が期待通りに機能します。

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 

ただし、3番目の関数ハングしているように見える - 具体的にはunquote(args)でハング:

iex(5)> Test.neg(-1) 

だから私の質問は、ハングアップしたようにNEGの呼び出しを引き起こすもの、ですか?私はアイデアを持っていると思うが、私はそれがなぜそうするのかを理解する(あるいは反論する)ことを望む。

第3の関数のwhen句は、defマクロに渡される引用された式の表現を変更し、これを処理するためのマクロの再加工に問題はありません。 negのために渡されdefinitioncontentは、次のとおりです。私たちはnegunquote(args)に到達すると、

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}]} 

args式を評価しようとしている私はnegへの呼び出しを含むリストであると考えていると、無限再帰的に結果ループ。これは正しいです?どのように私がこれをデバッグ/診断するかもしれないかについての指針は、さらに読むことへのリンクと同様に評価されるだろう。

答えて

3

我々はnegunquote(args)に達すると、私が、無限再帰ループでnegと結果への呼び出しを含むリストであると信じている、args式を評価しようとしています。これは正しいです?無限再帰になり

IO.puts("==> call: #{Tracer.dump_defn(:when, [neg(a), a < 0])}") 

はい、これはその行がにコンパイルするものです。

どのように私がデバッグ/診断するかについての説明は、今後の読者へのリンクと同様に高く評価されます。

quoteから返された値を|> Macro.to_string |> IO.putsに引き渡すことです。これは、そのquote式によって生成された正確なコードを出力します。

defmacro def({name, _, args} = definition, do: content) do 
    IO.puts("Definition: #{inspect definition}") 
    IO.puts("Content: #{inspect content}") 
    ast = 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 
    ast |> Macro.to_string |> IO.puts 
    ast 
end 

この版画:関数はハングなぜそれが明らかになり

Kernel.def(neg(a) when a < 0) do 
    IO.puts("==> call: #{Tracer.dump_defn(:when, [neg(a), a < 0])}") 
    result = -a 
    IO.puts("<== resp: #{inspect(result)}") 
    result 
end 

これは私があなたの仮説を確認するために何をしたかを正確です。この問題を解決するために


一つの方法は、実際の名前を抽出することです/名前は:when引数の場合:この後

... 
{name, args} = 
    case {name, args} do 
    {:when, [{name, _, args}, _]} -> {name, args} 
    {name, args} -> {name, args} 
    end 
quote do 

Test.neg/1作品:

iex(1)> Test.neg -1 
==> call: neg(-1) 
<== resp: 1 
1 
iex(2)> Test.neg -100 
==> call: neg(-100) 
<== resp: 100 
100 
    ... 
+0

おかげで、あなたの提案します修正は実際にどのように私はそれの周りに働いたので、私は何が起こっているのか理解し始めているいくつかの自信を与える! – Taufiq

関連する問題