elisp – ¿Puede una función o macro especificar advertencias del compilador de bytes?

Pregunta:

Estoy escribiendo una función que, en principio, toma un número arbitrario de argumentos. En la práctica, sin embargo, solo debe pasarse un número par de argumentos y, de lo contrario, producirá resultados no deseados.

Aquí hay un ejemplo ficticio para el contexto:

(defun my-caller (&rest args)
  (while args
    (call-other-function (pop args) (pop args))))

Cuando un archivo elisp se compila por bytes, el compilador de bytes lanza una advertencia cuando ve que se invoca una función con el número incorrecto de argumentos. Obviamente, eso nunca sucederá con my-caller , ya que está definido para tomar cualquier número.

Aún así, tal vez haya una propiedad de símbolo que pueda establecer, o una forma (declare) que pueda agregar a su definición. Algo para notificar al usuario que a esta función solo se le debe dar un número par de argumentos.

  1. ¿Hay alguna forma de informar al compilador de bytes de esta restricción?
  2. Si no es así , ¿es posible con una macro, en lugar de una función?

Respuesta:

EDITAR : Una mejor manera de hacer esto en Emacs reciente es definiendo una macro del compilador para verificar el número de argumentos. Mi respuesta original usando una macro normal se conserva a continuación, pero una macro de compilación es superior porque no evita pasar la función a funcall o apply en tiempo de ejecución.

En versiones recientes de Emacs, puede hacer esto definiendo una macro de compilación 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 del compilador debe devolver el formulario de llamada de función original sin cambios para su evaluación o compilación. Esto se hace usando un argumento &whole 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 ahora se pueden usar funcall y apply , pero omiten la comprobación de argumentos mediante la macro del compilador. A pesar de su nombre, las macros del compilador también parecen expandirse en el curso de la evaluación 'interpretada' a través de Cx Ce , Mx eval-buffer , por lo que obtendrá errores al evaluar y compilar este ejemplo.


A continuación, la respuesta original:

Así es como podría implementar la sugerencia de Jordon de "usar una macro que proporcione 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 se .elc ningún archivo .elc ), con un mensaje de error agradable 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 producir una advertencia en lugar de un error, permitiendo que la compilación continúe. (Gracias a Jordon por señalar esto en los comentarios).

Dado que las macros se expanden en tiempo de compilación, no hay ninguna penalización de tiempo de ejecución asociada con esta verificación. Por supuesto, no puede evitar que otras personas my-caller--function directamente a my-caller--function , pero al menos puede anunciarla como una función "privada" utilizando la convención del 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 a 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 de 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 quizás esta sea una compensación aceptable.

Leave a Comment

Your email address will not be published.

Scroll to Top

istanbul avukat

-

web tasarım