Compilación de un fichero Lisp
Tomemos por ejemplo la compilación de un fichero Lisp. El compilador Lisp procesa los formularios de nivel superior. Estos pueden ser formas arbitrarias Lisp, Defuns, DEFMACROS, DEFCLASS, llamadas a funciones, ...
Toda la historia de cómo funciona el compilador de archivos es demasiado compleja para explicar aquí, pero algunas cosas:
el compilador de archivos genera código para un formulario (DEFUN foo())
. Pero no ejecuta la forma difunta. Por lo tanto, durante la compilación se sabe que existe una función FOO
, pero el código de 'FOO' no está disponible durante la compilación. El compilador genera el código para el archivo compilado, pero no lo guarda en la memoria. No puede llamar a dicha función en tiempo de compilación.
para macros esto funciona ligeramente diferente: (DEFMACRO BAZ ...)
. El compilador de archivos no solo compilará la macro y se dará cuenta de que está allí, sino que también hará que la macro esté disponible en el momento de la compilación. Se carga en el compilador entorno.
Así imaginar la secuencia de formas en un archivo:
(defmacro baz ...)
(defun foo() (baz ...))
Esto funciona porque el compilador de archivos conoce la macro BAZ
y cuando se compila el código para FOO
, entonces se puede ampliar la forma de macro .
Ahora veamos el siguiente ejemplo:
(defun bar (form) ...)
(defmacro baz (form) (bar form))
(defun foo() (baz ...))
anterior no funcionará. Ahora la macro BAZ
usa la función BAR
llamándola. Cuando el compilador intenta compilar la función FOO
, no puede expandir la macro BAZ
, porque BAR
no se puede llamar, porque el código de BAR
no se carga en el entorno de tiempo de compilación.
Hay dos soluciones a este:
- compilación y carga
BAR
anterior utilizando un archivo separado.
- Uso EVAL-WHEN
Ejemplo para EVAL-WHEN
:
(eval-when (:compile-toplevel :execute :load-toplevel)
(defun bar (form) ...)
)
(defmacro baz (form) (bar form))
(defun foo() (baz ...))
Ahora el EVAL-WHEN
indica al compilador archivo para ejecutar realmente la forma DEFUN durante la compilación.El efecto de esto es: el compilador de archivos ahora conoce la definición de BAR
en tiempo de compilación. Por lo tanto, está disponible más adelante, cuando el compilador de archivos necesita llamar al BAR
durante la expansión de macro del uso de BAZ
.
Se podría usar solo :compile-toplevel
, cuando la función no sería necesaria después de la compilación del archivo. Si se usa más adelante, entonces debemos asegurarnos de que se cargue.
Así EVAL-WHEN
permite especificar si una determinada pieza de código se debe ejecutar
- durante la compilación de un archivo
- durante la carga de un archivo
- durante la ejecución
EVAL-WHEN
es no se usa tan a menudo en el código de usuario. Si lo usa, entonces debería preguntarse si realmente lo necesita.
Hola Rainer, pero ¿por qué tenemos que usar los indicadores: ejecutar y: cargar-nivel superior en este uso de eval-when? – utxeee
Rainer agradece nuevamente su respuesta al editar la respuesta anterior: D Pero mi duda persiste, por qué necesito usar los indicadores: load-top-level y: execute. De su texto puedo ver que deberíamos usar la bandera: load-top-level si la función se usa más tarde. Entonces, ¿por qué no necesitamos usar el eval-when con: load-top-level en todas las demás funciones que se usarán más adelante? – utxeee
@utxeee: load-top-level es un valor predeterminado durante la compilación, cuando no hay EVAL-WHEN. –