8

gensyms を使用するマクロをテストしたいと考えています。たとえば、これをテストしたい場合:

(defmacro m1
  [x f]
  `(let [x# ~x]
    (~f x#)))

マクロ展開が使える...

(macroexpand-1 '(m1 2 inc))

...取得するため...

(clojure.core/let [x__3289__auto__ 2] (inc x__3289__auto__))

人が正しいことを確認するのは簡単です。

しかし、これを実用的でクリーンな自動化された方法でテストするにはどうすればよいでしょうか? gensym は安定していません。

(はい、特定のマクロの例が説得力がないことはわかっていますが、それでも問題は公正です。)

Clojure 式はデータとして扱うことができる (ホモイコニック言語です) ので、結果を次のように分解できます。

(let [result (macroexpand-1 '(m1 2 inc))]
  (nth result 0) ; clojure.core/let
  (nth result 1) ; [x__3289__auto__ 2]
  ((nth result 1) 0) ; x__3289__auto__
  ((nth result 1) 0) ; 2
  (nth result 2) ; (inc x__3289__auto__)
  (nth (nth result 2) 0) ; inc
  (nth (nth result 2) 1) ; x__3289__auto__
  )

しかし、これは扱いにくいです。より良い方法はありますか?おそらく、便利なデータ構造の「検証」ライブラリがありますか? たぶん、破壊するとこれが簡単になりますか?ロジックプログラミング?

更新/解説:

「マクロ展開自体をテストしないでください」と言う経験豊富な人々のアドバイスに感謝しますが、それは私の質問に直接答えません。

マクロ展開をテストすることによってマクロを「単体テスト」することの何がそんなに悪いのですか? 展開をテストすることは理にかなっています -- そして実際、多くの人が自分のマクロをそのように REPL で「手作業で」テストしています -- では、それも自動的にテストしてみませんか? やらない正当な理由が見当たりません。マクロ展開をテストすることは、結果をテストすることよりも脆弱であることは認めますが、前者を実行することにはまだ価値があります。機能をテストすることもできます。両方を行うことができます。これはどちらかまたは両方の決定ではありません。

これが私の心理的な説明です。人々がマクロ展開をテストしない理由の 1 つは、現時点では少し面倒だからです。一般に、人は、本質的な価値とは無関係に、何かが難しいと思われる場合、それを行うことを正当化することがよくあります。はい - それがまさに私がこの質問をした理由です! もしそれが簡単だったら、人々はもっと頻繁にやると思います。もしそれが簡単なら、彼らは「やる価値がない」と答えて合理化する可能性は低いでしょう。

「複雑なマクロを書くべきではない」という議論も理解できます。もちろん。しかし、人々が「マクロをテストしないという文化を奨励すれば、複雑なマクロを書くことができなくなる」と考えないでほしいと思います。そのような議論はばかげているでしょう。複雑なマクロ展開がある場合、それが期待どおりに機能することをテストするのは賢明なことです。単純なミスからバグが発生する可能性があることにしばしば驚かされるため、私は個人的に単純なことでさえもテストを怠っていません。

4

2 に答える 2