Eval-when uses?
Asked Answered
C

1

15

After reading a lot of documentation regarding Lisp eval-when operator I still can't understand its uses, I know with this operator I can control the evaluation time of my expressions but I can't figure out any example where this may be applicable ?

Best Regards, utxeee.

Christoforo answered 20/5, 2012 at 15:17 Comment(0)
S
29

Compilation of a Lisp file

Take for example the compilation of a Lisp file. The Lisp compiler processes the top-level forms. These can be arbitrary Lisp forms, DEFUNs, DEFMACROS, DEFCLASS, function calls,...

The whole story how the file compiler works is too complex to explain here, but a few things:

  • the file compiler generates code for a (DEFUN foo () ) form. But it does not execute the defun form. Thus during compilation it is known that there is a function FOO, but the code of ˋFOOˋ is not available during the compilation. The compiler generates the code for the compiled file, but does not keep it in memory. You can't call such a function at compile time.

  • for macros this works slightly different: (DEFMACRO BAZ ...). The file compiler will not only compile the macro and note that it is there, but it will also make the macro available at compilation time. It is loaded into the compiler environment.

Thus imagine the sequence of forms in a file:

(defmacro baz ...)

(defun foo () (baz ...))

This works because the file compiler knows the macro BAZ and when it compiles the code for FOO, then it can expand the macro form.

Now let's look at the following example:

(defun bar (form) ...)

(defmacro baz (form) (bar form))

(defun foo () (baz ...))

Above will not work. Now the macro BAZ uses the function BAR by calling it. When the compiler tries to compile the function FOO, it can't expand the BAZ macro, because BAR can't be called, because the code of BAR is not loaded into the compile-time environment.

There are two solutions to this:

  1. compile and load BAR earlier using a separate file.
  2. Use EVAL-WHEN

Example for EVAL-WHEN:

 (eval-when (:compile-toplevel :execute :load-toplevel)
   (defun bar (form) ...)
 )

 (defmacro baz (form) (bar form))

 (defun foo () (baz ...))

Now the EVAL-WHEN instructs the file compiler to actually run the DEFUN form during compilation. The effect of this is: the file compiler now knows the definition of BAR at compile time. Thus it is available later, when the file compiler need to call BAR during macro expansion of the usage of BAZ.

One could use only :compile-toplevel, when the function would not be needed after the compilation of the file. If it is used later, then we need to make sure that it gets loaded.

So EVAL-WHEN allows to specify if a particular piece of code should be run

  • during compilation of a file
  • during loading of a file
  • during execution

EVAL-WHEN is not used that often in user code. If you use it, then you should ask yourself if you really need it.

Stenotypy answered 20/5, 2012 at 17:7 Comment(14)
Hi Rainer, but why we need to use the flags :execute and :load-top-level in this use of eval-when ?Christoforo
Rainer thanks again for your reply editing the previous answer :D But my doubt remains, why I need to use the flags :load-top-level and :execute. From your text I can see that we should use the flag :load-top-level if the function is used later. So why we don't need to use the eval-when with :load-top-level in all the other functions that will be used later ?Christoforo
@utxeee: load-top-level is a default during compilation, when there is no EVAL-WHEN.Stenotypy
Rainer, two more questions : (1) - if we have loaded the file from source code with your example there wasn't the need to use eval-when operator, because the bar definition was already known, right ? (2) - Why we need to use the keyword :execute - as far as I know this keyword is related with run-time but at run-time the definition of a function is already known too, right ?Christoforo
could you please fix the typo load-top-level ==> :load-toplevel? I had to dig into the source to find thatRunstadler
Regarding your first example: This works because the file compiler knows the macro BAZ and when it compiles the code for FOO, then it can expand the macro form., if the compiler doesn't execute the defun forms anyway, when is the expansion of baz going to happen, since it is in the non-top-level body form of foo.Tenia
@Tenia The file compiler will expand the macros in code, since it needs that in order to compile the code. It does not execute the DEFUN, but it compiles it and generates code (for example machine code) for the whole function definition form - including its subforms.Stenotypy
Are there also any aspects of eval-when controlled by the implementation?Tenia
With the above framework, one might wonder why the following compiles without error: (defun a () 2) (defun b () (+ (a) 4))Diplostemonous
@Diplostemonous What error would you expect and why?Stenotypy
@RainerJoswig, One might think that at compile time when the compiler is compiling defun b and the compiler sees the function call (a) inside defun b the compiler would issue an error because you stated that the code for defun a is not available at compile time. But, while macro names and their code are available at compile time to allow for the expansion of macros when they are called...Diplostemonous
defuns without macro calls inside them are not expanded at compile time, and therefore the call (a) inside defun b is not "expanded", i.e. (a) is not evaluated at compile time. The compiler knows defun a exists and that is enough to compile the code. Is that the correct explanation?Diplostemonous
@Diplostemonous : The file compiler does not need to know the code of A to compile B. You could even remove the code for A from the file and compile only B. All the compiler would do is to warn about an unknown function. The code would be compiled, though. If later a function A is defined/loaded, then the prior compiled function B will run just fine -> "late binding". What the file compiler would like to know, is that there is a function A in the file, but it does not need to execute the DEFUN for it and it would not need to save the code in the compile-time environment.Stenotypy
@Diplostemonous : additionally, DEFUN is a macro. Thus the macro expander for DEFUN runs at compile time. The DEFUN form in the file then can be expanded into code which includes an EVAL-WHEN form. Typically this will at compile-time notify the compiler of the function it compiles. See for example (macroexpand '(defun a () 2)) in SBCL. The DEFUN form expands into a progn with an eval-when (:compile-toplevel) as one of its subforms.Stenotypy

© 2022 - 2024 — McMap. All rights reserved.