4

I have code that generates code (like say-hello), and the simplest way I found to make functions with that generated code is to put a defun in an eval like so:

(defun say-hello () `(princ "test")) (eval `(defun test () ,(say-hello))) (test) 

This file runs fine on the command line:

sbcl --script test.lisp 

But complains when I try to compile it inside SBCL:

* (compile-file "test.lisp") ; compiling file "test.lisp" (written 19 APR 2018 01:05:19 PM): ; compiling (DEFUN SAY-HELLO ...) ; compiling (EVAL (SB-INT:QUASIQUOTE #)) ; compiling (TEST) ; file: test.lisp ; in: TEST ; (TEST) ; ; caught STYLE-WARNING: ; undefined function: TEST ; ; compilation unit finished ; Undefined function: ; TEST ; caught 1 STYLE-WARNING condition ; test.fasl written ; compilation finished in 0:00:00.031 #P"test.fasl" T NIL * 

Is there a way I can write my code to avoid this compilation error? Is there a better idiom for creating code from functions that generate code?

2 Answers 2

6

functions vs macros - the file compiler

The file compiler treats function and macro definitions slightly different:

  • functions: the file compiler compiles them and can use information about them. It dumps the generated code to the compiled fasl file. But it does not create an executable definition in the compile time environment.

  • macros: the file compiler compiles them and makes them available in the compile time environment. Thus the compiler can use an earlier in the same file defined macro to expand a use of that macro in the same file.

situations for forms when compiling a file

  • :compile-toplevel -> the file compile evals it when it compiles a file and the form is at toplevel

  • :load-toplevel -> the file compiler generates code so that the toplevel form is executed during load

Your code

If you want to use a function in the same file where it is defined, you need to tell the compiler to actually execute the definition at compile time:

(eval-when (:compile-toplevel :load-toplevel :execute) (defun fn-say-hello () `(princ "test"))) 

Later in the file a macro can use the function. The compiler knows about macro definitions automatically, but not about functions.

(defmacro say-hello () (fn-say-hello)) ; this is a function call, not returned code 

Then later you can use this macro in the file. The compiler will then expand that macro and run the function fn-say-hello - which then works, since we told the compiler about the function earlier.

(defun test () (say-hello)) (test) 
Sign up to request clarification or add additional context in comments.

2 Comments

So if test is defined as a macro, I need to put an EVAL-WHEN around say-hello to tell it to make it available at compile time, but if both are functions (the second one EVALd) then I need to put it around both to get it to happen at compile time.
@GustavBertram: all functions you want to use in the file - for example during macro expansion - need to be known. Either load them earlier or make sure than there is an EVAL-WHEN situation for them.
0

After playing around with EVAL-WHEN, I also realized that I can use the #. reader macro, rather than defining a once-off macro:

;; EVAL-WHEN makes say-hello available at compile time (eval-when (:compile-toplevel :load-toplevel :execute) ;; Define the function we want to use (defun say-hello () `(princ "test"))) (defun test () #.(say-hello)) ; Evals say-hello in-place at compile time (test) 

1 Comment

This is mostly only useful in the simple case, where there are no arguments to the generating function, since it for example does not seem the macro expansion environment - because it is done before macro expansion.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.