2017-02-21 34 views
0

私たちのclojureモジュールのバグを見つけながら、誰かが引数を渡した構文引用符付きマクロを見つけました。いくつかのデバッグの後、私は、引数が必要な値を持たないため、これが意図したとおりに動作しないことに気付きました。構文引用符付きマクロに引数を渡します

以下は、実際のコードで発生する状況の最小限の表現です。これはかなりよくポイントを説明する必要があります。

(def testargs {:arg1 "foo" 
       :arg2 "bar"}) 

(defmacro argtest 
    [arg1 arg2] 
    `(str [email protected](arg1 testargs) " " [email protected](arg2 testargs)) 

を予想通り、私はこのようなコードを呼び出す場合、これはうまく動作します:二つの引数を変数として渡された場合

(argtest :arg1 :arg2) 
=> "foo bar" 

しかし、それはしていませんとてもうまく機能:

(let [arg1 :arg1 arg2 :arg2] 
    (argtest arg1 arg2)) 
=> " " 

問題の底に取得するには、私は次のようにマクロを変更して、再度テストを実行しました:

(defmacro argtest2 
    [arg1 arg2] 
    (str arg1 " " arg2)) 

(argtest2 :arg1 :arg2) 
=> ":arg1 :arg2" 

(let [argument1 :arg1 argument2 :arg2] 
    (argtest2 argument1 argument2)) 
=> "argument1 argument2" 

ご覧のとおり、変数の値として渡されます。どうすればこれを避けることができますか?私はマクロを避けるためにコードを書き直そうとするべきですか、どういうわけか私はこの作業をすることができますか?

明白な解決策は、当然のことながら、このようなものになるだろう:

(defmacro argtest 
    [arg1 arg2] 
    `(str (~arg1 ~testargs) " " (~arg2 ~testargs))) 

しかし、マクロの出力を評価する前に、別のコードブロックに挿入されているので、これは不可能です。

+1

:多分私はあなたの問題をoversimplifyingてるのに広い文脈またはあなたが何をしようとして知らなくても、最も簡単な解決策は、あなたがすべてのケースで評価の引数をしたいように見えるよう、機能を使用することです'〜testargs' - >' testargs'はうまくいくはずですか? – cfrick

+1

(1)関数にマクロに変更できる場合は、それを行います。 (2)そうでない場合は、cfrickが '〜@ 'の代わりに'〜 'を使って言ったことをします。これは、あなたが示したものからは不要です。 –

+0

@cfrick:はい、ここに投稿したもので動作しますが、生成されたコードはあとで例外をスローし始めるスレッディングマクロに挿入されます... – UncleBob

答えて

2

フォームは、関数のようにマクロに渡される前に評価されません。

(defn argtest [arg1 arg2] 
    (str (arg1 testargs) " " (arg2 testargs))) 
+0

元のコードを書いたのであればその方法を選択しましたが、作成者はマップに格納された引用リストを使ってコードを生成し、生成されたコードをスレッドマクロに挿入することを選択しました...約6時間混乱を解きほぐしてみましたが、ここでの提案に従えば、全体を1つよりも少ない関数の単純なマップに置き換えました。みんなありがとう! – UncleBob

関連する問題