EDIT : un modo migliore per farlo negli ultimi Emacs consiste nel definire una macro del compilatore per verificare il numero di argomenti. La mia risposta originale usando una normale macro è conservata di seguito, ma una macro del compilatore è superiore perché non impedisce di passare la funzione funcall
ao apply
in fase di esecuzione.
Nelle versioni recenti di Emacs, puoi farlo definendo una macro del compilatore per la tua funzione che controlla il numero di argomenti e produce un avviso (o addirittura un errore) se non corrisponde. L'unica sottigliezza è che la macro del compilatore deve restituire il modulo di chiamata della funzione originale invariato per la valutazione o la compilazione. Questo viene fatto usando un &whole
argomento e restituendo il suo valore. Questo potrebbe essere realizzato in questo modo:
(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
Si noti che funcall
e apply
ora possono essere utilizzati, ma ignorano il controllo degli argomenti dalla macro del compilatore. Nonostante il loro nome, le macro compilatore sembrano anche essere ampliato nel corso del 'interpretato' di valutazione via C-xC-e, M-xeval-buffer, in modo da otterrete errori sulla valutazione e sulla compilazione di questo esempio.
Segue la risposta originale:
Ecco come è possibile implementare il suggerimento di Jordon di "utilizzare una macro che fornirà avvisi al momento dell'espansione". Risulta molto semplice:
(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)
Il tentativo di compilare quanto sopra in un file fallirà (non .elc
viene prodotto alcun file), con un bel messaggio di errore cliccabile nel registro di compilazione, che afferma:
test.el:14:1:Error: `my-caller' requires an even number of arguments
È inoltre possibile sostituire (error …)
con (byte-compile-warn …)
per produrre un avviso anziché un errore, consentendo la compilazione per continuare. (Grazie a Jordon per averlo sottolineato nei commenti).
Poiché le macro vengono espanse al momento della compilazione, non vi sono penalità di runtime associate a questo controllo. Naturalmente, non puoi impedire ad altre persone di chiamare my-caller--function
direttamente, ma puoi almeno pubblicizzarlo come una funzione "privata" usando la convenzione a doppio trattino.
Uno svantaggio notevole dell'utilizzo di una macro per questo scopo è che my-caller
non è più una funzione di prima classe: non è possibile passarla a funcall
o apply
in fase di esecuzione (o almeno non farà ciò che ci si aspetta). A tale proposito, questa soluzione non è altrettanto efficace della possibilità di dichiarare semplicemente un avviso del compilatore per una funzione reale. Ovviamente, l'utilizzo apply
renderebbe impossibile controllare il numero di argomenti passati alla funzione in fase di compilazione, quindi forse questo è un compromesso accettabile.