2016-04-12 12 views
1

で予期しない結果を取得し、私はマクロモジュール次のマクロでbind_quotedを理解していしようとしています:モジュールでbind_quoted声明

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.()) 

に従うよう

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 

は、次にシェルで、私が遊んで最後の行の結果として、私は持っている

calling remote API... 
============= 
:ok 
============= 
:ok 

しかし、私は期待して

============= 
calling remote API... 
:ok 
============= 
:ok 

bind_quotedは式を1回だけ実行することがわかります。
私の質問は、誰かが私が予期せぬ結果を得ている理由を説明してもらえますか?

+0

いくつかのメディアをクロスポストしないでください。本当に必要な場合は、中央の場所を選択し、他のポストにリンクさせてください! – NobbZ

+0

申し訳ありません、NobbZ。 –

+1

@zero_coding Chris McCordの "Metaprogramming Elixir"への投資が賢明な動きであるマクロの使い方を習得しようとしているとお考えですか?あなたがこれらの種類の質問にあなた自身を助けるのを助けるかもしれないように思われます。もちろん、ちょっとした提案です。 –

答えて

1

ただ、コピー&ペースト、すでにElixirforum – Get unexpected result with bind_quoted statementで与えられた私の答え:奇妙なものではなく、同様に文書化されていませんが、あなたはquote/2からbind_quotedオプションを使用する場合、それはこのようなものに展開され

を:

# your code 
quote bind_quoted: [foo: foo] do 
    IO.inspect foo 
end 

# what the compiler makes out of it in a first expansion pass of many! 
quote do 
    foo = unquote(foo) 
    IO.inspect foo 
end 

そうで何を注入しますあなたの例は次のとおりです。

このように見ると、出力がそのまま出力される理由を明示する必要があります。 bind_quotedを使用すると、はるかに少ないタイピングであっても

、私は強く、その使用を阻止します:

  1. あなたはアイテムがそのうちのトラックを失うかもしれないが、上記実行
  2. の順序をあなた緩いコントロールを例を与えました引用符で囲まれておらず、「内側」から来るもの
  3. 他のバインディングを手動で引用符で囲まないでください。

編集

そして、私はそれを忘れて前に...ログは副作用があってはならない(ログを除きます;))。すぐにリモートAPI呼び出しを行う「基本」関数からログを取ったり、今すぐ呼び出しているリモートAPI関数からログを取ったりしてください。しかし、ロガーがそれを実行するロガーに関数を渡してはいけません...マクロのコード注入のために、コンテキストの有害な変更があるかもしれません!

+0

まず、お返事いただきありがとうございます。私はIO.putsの戻り値を忘れてしまった:それはマクロ呼び出しの前に実行された。私はとても愚かです。 –

+0

マクロ呼び出しの前には実行されません。すべての 'unquote'で実行され、' bind_quoted'を使用して早期に 'unquote 'しています。 – NobbZ

1

第2 :okは、あなたのコードではなく、REPLによって印刷された式の結果です。 IO.puts:okを返します。 Macro.expandコードで確認できるので、式は正確に1回だけ評価されます。

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 
) 
) 
+0

彼が不思議に思っているのは2番目の「OK」ではありません。アウトプットの最初の2行が予想通りにスワップされているのは事実です。しかし、実現するために私に「diff」を実行してもらいました;) – NobbZ

+0

ああ。はい、私は完全に間違っています。確かに、私はこの答えを削除する必要がありますか?... –

+0

実際には、あなたの答えは本当に、私は簡略化された方法で提示されているもの、マクロに与えられた引数は与えられた ' :do'ブロック。おそらく、あなたの先頭の文章を言い換えて、これを質問の正当な答えにすることができますか? – NobbZ