どうしたの
これは、現在の名前空間の概念が Clojure でどのように機能するかによって発生します。macroexpand-1
現在の名前空間で引数を展開します。
REPL では、これはuser
; 名前空間でマクロを定義しているuser
場合、その名前空間で呼び出すmacroexpand-1
と、すべて問題ありません。
名前空間、:gen-class'd
または実際には他の名前空間では、コンパイル時の現在の名前空間はその名前空間そのものです。ただし、後でこの名前空間で定義されたコードを呼び出すと、その時点で適切な名前空間が現在の名前空間になります。コンパイルされると、それは他の名前空間になる可能性があります。
最後に、アプリの実行時のデフォルトの現在の名前空間はuser
です。
これを確認するには、マクロを別の名前空間に移動して、関数も定義し、use-the-macro
この関数を最上位で呼び出します。'd 名前空間は、:gen-class
マクロの名前空間を要求または使用する必要があります。次にlein run
、(マクロの名前空間のコンパイル時に) 期待するものを 1 回出力し、展開されていない形式を 2 回 (マクロの名前空間がrequire
メインの名前空間によって d され、次に が-main
呼び出されたときにuse-the-macro
) 出力します。
ソリューション
Clojure REPL はbinding
;を使用して現在の名前空間を制御します。同じことができます:
(binding [*ns* (the-ns 'scratchpad.core)]
(prn (macroexpand-1 ...)))
quote in の代わりに syntax-quote を使用することもできます-main
。
(defn -main [& args]
(prn (macroexpand-1 `...)))
^- changed this
もちろん、以外のシンボルunless
が関係している場合は、それらを出力で名前空間修飾する必要があるかどうかを決定し、場合によってはプレフィックスを付ける必要があります~'
。ただし、これがポイントです。syntax-quote は、ほとんど「名前空間に依存しない」コードを生成するのに適しています (これが、便利な構文に加えて、マクロを作成するのに非常に優れている理由です)。
別の可能な「修正」(Clojure 1.5.1でテスト済み)は、へのin-ns
呼び出しを追加すること-main
です:
(defn -main [& args]
(in-ns 'scratchpad.core)
(prn (macroexpand-1 '...)))
^- no change here this time
と同様にbinding
、このようにして、元の名前空間で元のフォームの拡張を実際に取得しています。