Saya akan berterus terang: Saya tidak mengerti apa artinya "gunakan untuk sintaks, bukan untuk semantik". Inilah sebabnya mengapa bagian dari jawaban ini akan membahas jawaban lunaryon , dan bagian lainnya adalah usaha saya untuk menjawab pertanyaan awal.
Ketika kata "semantik" digunakan dalam pemrograman, itu merujuk pada cara penulis bahasa memilih untuk menafsirkan bahasa mereka. Ini biasanya bermuara pada seperangkat formula yang mengubah aturan tata bahasa menjadi beberapa aturan lain yang diasumsikan pembaca sudah terbiasa. Misalnya, Plotkin dalam bukunya Structural Approach to Operational Semantics menggunakan bahasa derivasi logis untuk menjelaskan apa yang dievaluasi sintaksis abstraknya (apa artinya menjalankan program). Dengan kata lain, jika sintaks adalah yang merupakan bahasa pemrograman pada tingkat tertentu, maka itu harus memiliki beberapa semantik, jika tidak, yah ... bukan bahasa pemrograman.
Tapi, mari kita lupakan definisi formal, lagipula, penting untuk memahami maksudnya. Jadi, sepertinya lunaryon akan mendorong pembacanya untuk menggunakan makro ketika tidak ada arti baru yang ditambahkan ke program, tetapi hanya semacam singkatan. Bagi saya ini kedengarannya aneh dan sangat kontras dengan bagaimana makro sebenarnya digunakan. Di bawah ini adalah contoh-contoh yang dengan jelas menciptakan makna baru:
defun
dan teman-teman, yang merupakan bagian penting dari bahasa tidak dapat dijelaskan dalam istilah yang sama dengan yang Anda gambarkan panggilan fungsi.
setf
aturan yang menggambarkan bagaimana fungsi bekerja tidak memadai untuk menggambarkan efek dari ekspresi (setf (aref x y) z)
.
with-output-to-string
juga mengubah semantik kode di dalam makro. Demikian pula, with-current-buffer
dan banyak with-
makro lainnya .
dotimes
dan yang serupa tidak dapat dijelaskan dalam hal fungsi.
ignore-errors
mengubah semantik kode yang dibungkusnya.
Ada sejumlah makro yang didefinisikan dalam cl-lib
paket, eieio
paket dan beberapa perpustakaan lain yang hampir ada di mana-mana yang digunakan dalam Emacs Lisp yang, sementara mungkin terlihat seperti bentuk fungsi memiliki interpretasi yang berbeda.
Jadi, jika ada, makro adalah alat untuk memperkenalkan semantik baru ke dalam bahasa. Saya tidak bisa memikirkan cara alternatif untuk melakukan itu (setidaknya tidak di Emacs Lisp).
Ketika saya tidak akan menggunakan makro:
Ketika mereka tidak memperkenalkan konstruksi baru, dengan semantik baru (yaitu fungsi akan melakukan pekerjaan sama baiknya).
Saat ini hanya semacam kenyamanan (yaitu membuat nama makro mvb
yang diperluas menjadi cl-multiple-value-bind
hanya untuk membuatnya lebih pendek).
Ketika saya berharap banyak kode penanganan kesalahan disembunyikan oleh makro (seperti telah dicatat, makro sulit untuk di-debug).
Ketika saya lebih suka makro daripada fungsi:
Ketika konsep khusus domain dikaburkan oleh panggilan fungsi. Sebagai contoh, jika seseorang perlu meneruskan context
argumen yang sama ke banyak fungsi yang perlu dipanggil secara berurutan, saya lebih suka untuk membungkusnya dalam makro, di mana context
argumen itu tersembunyi.
Ketika kode generik dalam bentuk fungsi terlalu bertele-tele (konstruksi iterasi kosong di Emacs Lisp terlalu bertele-tele dengan seleraku dan membuat programmer tidak perlu untuk detail implementasi tingkat rendah).
Ilustrasi
Di bawah ini adalah versi dipermudah dimaksudkan untuk memberikan satu intuisi tentang apa yang dimaksud dengan semantik dalam ilmu komputer.
Misalkan Anda memiliki bahasa pemrograman yang sangat sederhana hanya dengan aturan sintaks ini:
variable := 1 | 0
operation := +
expression := variable | (expression operation expression)
dengan bahasa ini kita dapat membuat "program" seperti (1+0)+1
atau 0
atau lainnya ((0+0))
.
Tetapi selama kami tidak memberikan aturan semantik, "program" ini tidak ada artinya.
Misalkan kita sekarang melengkapi bahasa kita dengan aturan (semantik):
0+0 0+1 1+0 1+1 (exp)
---, ---, ---, ---, -----
0 1+0 1 0 exp
Sekarang kita benar-benar dapat menghitung menggunakan bahasa ini. Artinya, kita dapat mengambil representasi sintaksis, memahaminya secara abstrak (dengan kata lain, mengubahnya menjadi sintaksis abstrak), kemudian menggunakan aturan semantik untuk memanipulasi representasi ini.
Ketika kita berbicara tentang keluarga Lisp bahasa, aturan semantik dasar evaluasi fungsi kira-kira adalah lambda-calculus:
(f x) (lambda x y) x
-----, ------------, ---
fx ^x.y x
Mekanisme mengutip dan ekspansi makro juga dikenal sebagai alat meta-pemrograman, yaitu mereka memungkinkan seseorang untuk berbicara tentang program, bukan hanya pemrograman. Mereka mencapai ini dengan membuat semantik baru menggunakan "lapisan meta". Contoh makro sederhana tersebut adalah:
(a . b)
-------
(cons a b)
Ini sebenarnya bukan makro di Emacs Lisp, tetapi bisa saja. Saya memilih hanya demi kesederhanaan. Perhatikan bahwa tidak ada aturan semantik yang didefinisikan di atas yang berlaku untuk kasus ini karena kandidat terdekat (f x)
mengartikannya f
sebagai fungsi, sementara a
itu tidak harus berfungsi.