Gustav Bertram
Gustav Bertram

Reputation: 14901

How can I avoid this compilation error?

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?

Upvotes: 4

Views: 740

Answers (2)

Gustav Bertram
Gustav Bertram

Reputation: 14901

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)

Upvotes: 0

Rainer Joswig
Rainer Joswig

Reputation: 139241

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)

Upvotes: 6

Related Questions