Bagaimana saya bisa menggunakan macroexpand-1 dari file sumber alih-alih REPL

Apa cara yang tepat untuk menggunakan macroexpand-1 untuk menguji makro Clojure saat tidak bekerja di REPL?


person Alan Thompson    schedule 06.05.2017    source sumber


Jawaban (2)


Masalahnya adalah ekspresi Anda di luar deftest dijalankan pada waktu kompilasi, sementara *ns* terikat, dan di dalam deftest dijalankan kemudian, saat runtime, di mana *ns* tidak terikat.

Mengapa ini penting? Karena macroexpand perlu menyelesaikan simbol iiinc di namespace saat ini untuk memutuskan apakah itu makro atau bukan, dan untuk menemukan definisinya apakah itu makro, untuk memanggilnya. Jadi, Anda melihat macroexpand Anda berfungsi pada waktu kompilasi tetapi tidak pada waktu proses.

Apa solusinya? Tentunya untuk tidak menjalankan pengujian Anda pada waktu kompilasi! Sebaliknya, Anda harus mengkualifikasi namespace formulir Anda dengan benar, sehingga formulir tersebut tidak bergantung pada kenyamanan waktu kompilasi *ns*. Anda dapat melakukannya dengan tangan, dengan menulis

(deftest t-stuff
  (println "(macroexpand-1 '(my.ns/iiinc 2))  =>" (macroexpand-1 '(my.ns/iiinc 2)))))

Namun, solusi yang tepat adalah melakukan apa yang harus Anda selalu lakukan ketika mengutip formulir yang dimaksudkan untuk evaluasi nanti, sama seperti ketika Anda menulis makro: gunakan kutipan sintaksis, bukan kutipan biasa. Dengan cara ini kompiler mengetahui namespace yang dimaksudkan untuk Anda pada waktu kompilasi, dan menyisipkannya ke dalam formulir sehingga masih ada saat runtime:

(deftest t-stuff
  (println "(macroexpand-1 `(iiinc 2))  =>" (macroexpand-1 `(iiinc 2)))))
person amalloy    schedule 06.05.2017

Misalkan kita ingin menguji makro yang menambahkan 3 pada nilai apa pun:

(defmacro iiinc [x]
  `(+ 3 ~x))

Daripada bekerja di REPL, saya biasanya lebih suka menggunakan editor teks/IDE favorit saya untuk mengembangkan kode, dan menggunakan plugin lein test-refresh untuk terus menjalankan pengujian unit saya. Namun, ini tidak berhasil ketika mencoba menggunakan macroexpand-1 untuk mengembangkan makro baru secara berulang.

Masalahnya sepertinya ada konflik antara makro macroexpand-1 dan deftest. Jadi, solusinya adalah menghindari penggunaan macroexpand-1 di dalam formulir (deftest ...). Namun, ini berfungsi dengan baik di luar deftest, meskipun masih dalam file sumber pengujian unit. Berikut ini contohnya:

; source file tst.clj.core

(newline)
(println "This works when not in (deftest ...)")
(println "(macroexpand-1 '(iiinc 2))  =>" (macroexpand-1 '(iiinc 2)))
    
(deftest t-stuff
  (newline)
  (println "But it is broken inside (deftest ...)")
  (println "(macroexpand-1 '(iiinc 2))  =>" (macroexpand-1 '(iiinc 2)))

  (newline)
  (println "However, we can use the macro itself fine in our tests")
  (println "  (iiinc 2) =>" (iiinc 2))
  (is (= 5 (iiinc 2))))  ; unit test works fine

Hasil di atas adalah:

This works when not in (deftest ...)
(macroexpand-1 '(iiinc 2))  => (clojure.core/+ 3 2)

Testing tst.clj.core

But it is broken inside (deftest ...)
(macroexpand-1 '(iiinc 2))  => (iiinc 2)

However, we can use the macro itself fine in our tests
  (iiinc 2) => 5

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Nota bene

Silakan lihat jawaban berikut untuk pembahasan lebih lengkap tentang cara menulis makro di Clojure:

Bagaimana cara menulis makro threading Clojure?

person Alan Thompson    schedule 06.05.2017
comment
Jawaban yang bagus, tapi menurut saya beberapa konten ini harus ada dalam pertanyaan itu sendiri, karena ini menjelaskan apa yang Anda coba lakukan tetapi tidak berhasil. Tanpa konteks jawaban ini, tidak ada orang lain yang dapat menjawab pertanyaan singkat Anda. - person amalloy; 06.05.2017
comment
Melihat kembali hal ini empat tahun kemudian, saya sekarang berpikir seluruh jawaban harus dipindahkan ke dalam pertanyaan. Anda tidak memberikan jawaban apa pun atas pertanyaan Anda Bagaimana cara menguji makro dengan macroexpand?; Anda hanya menyimpulkan bahwa itu tidak berhasil. Ini sangat menyeluruh, sebagai sebuah pertanyaan, dan saya pikir jawaban saya memberikan jawaban yang bagus untuk itu, saya hanya tidak mengerti bagaimana semua ini merupakan jawaban. - person amalloy; 02.02.2021