EDIT : Cara yang lebih baik untuk melakukan ini di Emacs baru-baru ini adalah dengan mendefinisikan makro kompiler untuk memeriksa jumlah argumen. Jawaban asli saya menggunakan makro normal dipertahankan di bawah ini, tetapi makro kompiler lebih unggul karena tidak mencegah melewatkan fungsi ke funcall
atau apply
saat runtime.
Di versi terbaru Emacs, Anda bisa melakukan ini dengan mendefinisikan kompiler-makro untuk fungsi Anda yang memeriksa jumlah argumen dan menghasilkan peringatan (atau bahkan kesalahan) jika tidak cocok. Satu-satunya kehalusan adalah bahwa makro kompiler harus mengembalikan bentuk panggilan fungsi asli tidak berubah untuk evaluasi atau kompilasi. Ini dilakukan dengan menggunakan &whole
argumen dan mengembalikan nilainya. Ini bisa dicapai seperti ini:
(require 'cl-lib)
(defun my-caller (&rest args)
(while args
(message "%S %S" (pop args) (pop args))))
(define-compiler-macro my-caller (&whole form &rest args)
(when (not (cl-evenp (length args)))
(byte-compile-warn "`my-caller' requires an even number of arguments"))
form)
(my-caller 1 2 3 4)
(my-caller 1 2)
(funcall #'my-caller 1 2 3 4) ; ok
(apply #'my-caller '(1 2)) ; also ok
(my-caller 1) ; produces a warning
(funcall #'my-caller 1 2 3) ; no warning!
(apply #'my-caller '(1 2 3)) ; also no warning
Catat itu funcall
dan apply
sekarang bisa digunakan, tetapi mereka mem-bypass pengecekan oleh kompiler makro. Meskipun nama mereka, macro compiler juga tampaknya akan diperluas dalam perjalanan 'ditafsirkan' evaluasi melalui C-xC-e, M-xeval-buffer, sehingga Anda akan mendapatkan kesalahan pada evaluasi serta pada kompilasi contoh ini.
Jawaban asli berikut:
Inilah cara Anda menerapkan saran Jordon untuk "menggunakan makro yang akan memberikan peringatan pada waktu ekspansi". Ternyata sangat mudah:
(require 'cl-lib)
(defmacro my-caller (&rest args)
(if (cl-evenp (length args))
`(my-caller--function ,@args)
(error "Function `my-caller' requires an even number of arguments")))
(defun my-caller--function (&rest args)
;; function body goes here
args)
(my-caller 1 2 3 4)
(my-caller 1 2 3)
Mencoba mengkompilasi hal di atas dalam file akan gagal (tidak ada .elc
file yang dihasilkan), dengan pesan kesalahan yang dapat diklik bagus di kompilasi log, menyatakan:
test.el:14:1:Error: `my-caller' requires an even number of arguments
Anda juga dapat mengganti (error …)
dengan (byte-compile-warn …)
untuk menghasilkan peringatan alih-alih kesalahan, memungkinkan kompilasi untuk melanjutkan. (Terima kasih kepada Jordon karena menunjukkan ini dalam komentar).
Karena makro diperluas pada waktu kompilasi, tidak ada penalti waktu berjalan terkait dengan pemeriksaan ini. Tentu saja, Anda tidak dapat menghentikan panggilan orang lain my-caller--function
secara langsung, tetapi setidaknya Anda dapat mengiklankannya sebagai fungsi "pribadi" menggunakan konvensi hyphen-double.
Kerugian penting menggunakan makro untuk tujuan ini adalah bahwa my-caller
tidak lagi fungsi kelas satu: Anda tidak bisa meneruskannya ke funcall
atau apply
saat runtime (atau setidaknya itu tidak akan melakukan apa yang Anda harapkan). Dalam hal itu, solusi ini tidak sebaik bisa dengan mudah mendeklarasikan peringatan kompiler untuk fungsi yang sebenarnya. Tentu saja, menggunakan tidak apply
akan memungkinkan untuk memeriksa jumlah argumen yang diteruskan ke fungsi pada waktu kompilasi, jadi mungkin ini merupakan trade-off yang dapat diterima.