Common Lisp memungkinkan Anda untuk menulis makro yang melakukan transformasi sumber apa pun yang Anda inginkan.
Skema memberi Anda sistem pencocokan pola higienis yang memungkinkan Anda melakukan transformasi juga. Seberapa berguna makro dalam praktik? Paul Graham mengatakan dalam Beating the Averages bahwa:
Kode sumber editor Viaweb mungkin sekitar 20-25% makro.
Hal-hal macam apa yang akhirnya orang lakukan dengan makro?
Jawaban:
Lihatlah posting ini oleh Matthias Felleisen ke daftar diskusi LL1 pada tahun 2002. Dia menyarankan tiga kegunaan utama untuk makro:
sumber
Saya kebanyakan menggunakan makro untuk menambahkan konstruksi bahasa baru yang menghemat waktu, yang kalau tidak akan memerlukan banyak kode boilerplate.
Sebagai contoh, saya baru-baru ini mendapati diri saya ingin imperatif
for-loop
mirip dengan C ++ / Java. Namun, sebagai bahasa fungsional, Clojure tidak datang dengan satu di luar kotak. Jadi saya hanya mengimplementasikannya sebagai makro:Dan sekarang saya bisa melakukan:
Dan begitulah - konstruksi bahasa kompilasi waktu-tujuan baru baru dalam enam baris kode.
sumber
Ekstensi bahasa tulisan atau DSL.
Untuk merasakan hal ini dalam bahasa mirip Lisp, pelajarilah Racket , yang memiliki beberapa varian bahasa: Typed Racket, R6RS, dan Datalog.
Lihat juga bahasa Boo, yang memberi Anda akses ke pipa penyusun untuk tujuan khusus membuat Bahasa Khusus Domain melalui makro.
sumber
Berikut ini beberapa contohnya:
Skema:
define
untuk definisi fungsi. Pada dasarnya itu membuat cara yang lebih pendek untuk mendefinisikan suatu fungsi.let
untuk membuat variabel dengan cakupan leksikal.Clojure:
defn
, menurut dokumennya:Sama seperti (nama def (fn [params *] exprs *)) atau (nama def (fn ([params *] exprs *) +)) dengan setiap string atau attr yang ditambahkan ke var metadata
for
: daftar pemahamandefmacro
: ironis?defmethod
,defmulti
: bekerja dengan multi-metodens
Banyak makro ini membuatnya lebih mudah untuk menulis kode pada tingkat yang lebih abstrak. Saya menganggap makro mirip, dalam banyak hal, dengan sintaks dalam non-Lisps.
Incanter plotting library menyediakan makro untuk beberapa manipulasi data yang kompleks.
sumber
Makro berguna untuk menyematkan beberapa pola.
Sebagai contoh, Common Lisp tidak mendefinisikan
while
loop tetapi memilikido
yang dapat digunakan untuk mendefinisikannya.Ini adalah contoh dari On Lisp .
Ini akan mencetak "12345678910", dan jika Anda mencoba melihat apa yang terjadi dengan
macroexpand-1
:Ini akan mengembalikan:
Ini adalah makro sederhana, tetapi seperti yang dikatakan sebelumnya, mereka biasanya digunakan untuk mendefinisikan bahasa atau DSL baru, tetapi dari contoh sederhana ini Anda sudah dapat mencoba membayangkan apa yang dapat Anda lakukan dengan mereka.
The
loop
makro adalah contoh yang baik dari apa yang macro bisa lakukan.Common Lisp memiliki jenis makro lain yang disebut pembaca makro yang dapat digunakan untuk memodifikasi cara pembaca menginterpretasikan kode, yaitu Anda dapat menggunakannya untuk menggunakan # {dan #} memiliki pembatas seperti # (dan #).
sumber
Inilah yang saya gunakan untuk debugging (di Clojure):
Saya harus berurusan dengan tabel hash linting tangan di C + +, di mana
get
metode mengambil referensi string non-const sebagai argumen, yang berarti saya tidak bisa menyebutnya dengan literal. Untuk membuatnya lebih mudah untuk ditangani, saya menulis sesuatu seperti berikut:Sementara sesuatu seperti masalah ini tidak mungkin muncul di lisp, saya merasa sangat baik bahwa Anda dapat memiliki makro yang tidak mengevaluasi argumen mereka dua kali, misalnya dengan memperkenalkan pengikatan nyata . (Diakui, di sini saya bisa mengatasinya).
Saya juga menggunakan cara membungkus barang yang jelek dan jelek
do ... while (false)
sehingga Anda bisa menggunakannya di bagian waktu dan jika masih ada bagian lain seperti yang diharapkan. Anda tidak memerlukan ini dalam lisp, yang merupakan fungsi dari makro yang beroperasi pada pohon sintaks daripada string (atau urutan token, saya pikir, dalam kasus C dan C ++) yang kemudian menjalani parsing.Ada beberapa makro threading bawaan yang dapat digunakan untuk mengatur ulang kode Anda sehingga terbaca lebih bersih ('threading' seperti dalam 'menabur kode Anda bersama-sama', bukan paralelisme). Sebagai contoh:
Ia mengambil bentuk pertama
(range 6)
,, dan menjadikannya argumen terakhir dari bentuk berikutnya(filter even?)
,, yang pada gilirannya dijadikan argumen terakhir dari bentuk berikutnya dan seterusnya, sehingga di atas akan ditulis ulang menjadiSaya pikir yang pertama berbunyi lebih jelas: "ambil data ini, lakukan ini, lalu lakukan itu, lalu lakukan yang lain dan kita selesai", tapi itu subjektif; sesuatu yang secara objektif benar adalah bahwa Anda membaca operasi dalam urutan yang mereka lakukan (mengabaikan kemalasan).
Ada juga varian yang menyisipkan bentuk sebelumnya sebagai argumen pertama (bukan terakhir). Satu kasus penggunaan adalah aritmatika:
Dibaca sebagai "ambil 17, kurangi 2 dan bagi 3".
Berbicara tentang aritmatika, Anda dapat menulis makro yang tidak menguraikan notasi parsing, sehingga Anda bisa mengatakan misalnya
(infix (17 - 2) / 3)
dan itu akan memuntahkan(/ (- 17 2) 3)
yang memiliki kelemahan yaitu kurang dapat dibaca dan keuntungan menjadi ekspresi pelat yang valid. Itu bagian sublingu DSL / data.sumber