EDITAR : Una mejor manera de hacer esto en Emacs reciente es definiendo una macro compiladora para verificar el número de argumentos. Mi respuesta original utilizando una macro normal se conserva a continuación, pero un compilador-macro es superior porque no impide pasar a la función funcall
o apply
en tiempo de ejecución.
En versiones recientes de Emacs, puede hacer esto definiendo una macro compiladora para su función que verifica el número de argumentos y produce una advertencia (o incluso un error) si no coincide. La única sutileza es que la macro compiladora debe devolver el formulario de llamada de función original sin cambios para evaluación o compilación. Esto se hace usando un &whole
argumento y devolviendo su valor. Esto podría lograrse así:
(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
Tenga en cuenta que funcall
y apply
ahora se puede usar, pero omiten la comprobación de argumentos por parte de la macro compiladora. A pesar de su nombre, las macros del compilador también parecen ser ampliado en el curso de 'interpretado' evaluación a través de C-xC-e, M-xeval-buffer, por lo que obtendrá errores en la evaluación, así como en la elaboración de este ejemplo.
La respuesta original sigue:
Así es como podría implementar la sugerencia de Jordon de "usar una macro que proporcionará advertencias en el momento de la expansión". Resulta muy fácil:
(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)
Intentar compilar lo anterior en un archivo fallará (no .elc
se produce ningún archivo), con un bonito mensaje de error en el que se puede hacer clic en el registro de compilación, que indica:
test.el:14:1:Error: `my-caller' requires an even number of arguments
También puede reemplazar (error …)
con (byte-compile-warn …)
para generar una advertencia en lugar de un error, permitiendo que continúe la compilación. (Gracias a Jordon por señalar esto en los comentarios).
Como las macros se expanden en el momento de la compilación, no hay penalización en tiempo de ejecución asociada con esta verificación. Por supuesto, no puede evitar que otras personas llamen my-caller--function
directamente, pero al menos puede anunciarlo como una función "privada" utilizando la convención de doble guión.
Una desventaja notable de usar una macro para este propósito es que my-caller
ya no es una función de primera clase: no puede pasarla funcall
o apply
en tiempo de ejecución (o al menos no hará lo que espera). En ese sentido, esta solución no es tan buena como poder simplemente declarar una advertencia del compilador para una función real. Por supuesto, el uso apply
haría imposible verificar el número de argumentos que se pasan a la función en tiempo de compilación de todos modos, por lo que tal vez sea una compensación aceptable.