Search code examples
macrosschemeguile

Scheme can't find function inside macro while compile


I have sample code like this:

#!/usr/bin/guile -s
!#

(define (process body)
    (list 'list (map (lambda (lst)
                       (list 'quote (car lst)))
                       body)))

(defmacro macro (body)
  (list 'quote (process body)))

(display (macro ((foo bar) (bar baz))))
(newline)

it run but I've got error from compiler

ERROR: Unbound variable: process

functions inside macros should be allowed, why I got this error?


Solution

  • Functions inside macros are allowed in Guile and in most other Scheme dialects.

    However, the crucial question is: Which functions are available for a macro to call out during the expansion process?


    Think of it this way: When the compiler is processing your code, it is first focused on turning your source code into something that can be run at some point in the future. But the compiler might not necessarily be able to execute those same functions right now while it is compiling them, at the same time that your macro is running and expanding the source code in general.

    • Why wouldn't such a function be available? Well, one example would be: What if the function's body used the macro you are in the midst of defining? Then you would have a little chicken/egg problem. The function would need to run the macro to be compiled (since the macro use in the body needs to be expanded, at compile-time) ... But the macro would need the compiled function available in order to even run!

    (Furthermore, there might be some functions that you only want to be available at compile-time, as a helper for your macros, but that you do not want to be available at run-time, so that it will not be included in your program executable when you deploy it, as that would waste space in the deployed binary.)

    One of my favorite papers describing this problem, and the particular solution adopted by MzScheme (now known as Racket), is the "You Want It When" paper by Matthew Flatt.


    So, this is a problem that any Scheme dialect with a procedural macro system has to deal with in some way, and Guile is no exception.

    In Guile's case, one fix that is directly documented in the Guile manual is to use the eval-when special form, which allows you to specify at what phases a particular definition is meant to be available.

    (The "You Want It When" paper referenced above describes some problems with eval-when, but since it is what the Guile manual documents, I'm going to stick with it for now. I do recommend that after you understand eval-when, that you then look into Racket's solution, and see if Guile offers anything similar.)


    So in your case, since you want the process function to be available at compile-time (for use in the macro definition), you could write:

    #!/usr/bin/guile -s
    !#
    
    (eval-when (expand)
      (define (process body)
        (list 'list (map (lambda (lst)
                           (list 'quote (car lst)))
                         body))))
    
    (defmacro macro (body)
      (list 'quote (process body)))
    
    (display (macro ((foo bar) (bar baz))))
    (newline)