さて...まず、clojureのマクロ構文を読んでおくことをお勧めします。ここでは少しプリマーを提供しますが、詳しくは説明しません。
まず最初に、これがあなたのマクロです。
(defmacro safe [bindings? & forms]
(let [bindings (if (and (even? (count bindings?)) (vector? bindings?))
bindings? nil)
forms (if bindings forms (cons bindings? forms))
except `(catch Exception e# e#)]
(if bindings
`(let ~bindings (try ~@forms ~except))
`(try ~@forms ~except))))
そして今、ウォークスルーのために。
Clojureの(let)マクロは、偶数の引数を持つベクトルを要求し、destructuringと呼ばれるいくつかの非常に興味深い動作をサポートします。このマクロの目的のために、有効なバインディング引数は最初はベクトルで、次に偶数の長さであると想定しています。(let)の評価はこれと同じチェックを実行しますが、最初のフォームがバインディングではなく評価対象のフォームである可能性があり、その場合は異なる動作を示す可能性があるため、このマクロはそれを実行する必要があります。
マクロ自体に関しては、引数を処理するために(let)を使用します。この記号bindings
は、バインディングの存在を示すことと、バインディングベクトルが存在する場合はそれを取得することの2つの目的を果たします。forms
は、引数の最初のバインディング(clojureで実行できます)からbindings
、エラーが含まれる環境で実行するフォームシーケンス全体の影響を受ける値に再定義されます。シンボルは実際には必要ありません。except
各拡張ケースでその(キャッチ)フォームを再記述するというコードの重複を回避するだけです。
私が使用する記号`(バッククォートまたはバックティックとして知られている)は、ここでは通常のクォート(')と同等ですが、clojureを使用すると、クォートされたフォームではなく、バッククォートされたフォーム内でマクロ展開構文を使用できます。マクロ構文には、〜(引用符で囲まれていない)演算子と〜@(引用符で囲まれていない挿入)uperatorが含まれています。これらの3ビットの表記法を使用して、両方の望ましいケースを定義しました。バインディングフォームと試行するフォームを挿入するバインディングフォームを使用したletと、単純なtryonlyの場合です。
条件付きを削除して生成することができます
(defmacro safe [bindings? & forms]
(let [bindings (if (and (even? (count bindings?)) (vector? bindings?))
bindings? [])
forms (if-not (empty? bindings)
forms (cons bindings? forms))
except `(catch Exception e# e#)]
`(let ~bindings (try ~@forms ~except))))
しかし、結合形式がない場合は、余分な(let)があります。