The short answer is that you need a (require (for-template racket/base))
in your utilities submodule:

  (module utilities racket/base
    (provide compile-test)

    (require (for-template racket/base))

    (define (compile-test)
      #`(lambda (i) (displayln `(input: ,i)))))

But this answer probably isn’t especially helpful towards debugging
similar problems in the future, so let me give a longer explanation.

Racket’s macro system has phases. Phase 0 corresponds to runtime, phase
1 corresponds to phase 0’s compile-time, phase 2 corresponds to phase
1’s compile-time, etc. This space of phases is unbounded. Each phase
contains a completely distinct set of bindings, so when you write, say,
`let` at phase 0, it isn’t necessarily the same `let` as the one you use
at phase 1.

The code you write in the body of a define-syntax definition is in phase
1, since it is evaluated at compile-time. In your top-level module, you
use #lang racket, which happens to provide the bindings from racket/base
at both phase 0 and phase 1. This is why you can use `let` from
racket/base inside your this-works macro — it was provided at that phase
by #lang racket. The code in your template, in this case #`(lambda (i)
....), ends up getting evaluated at runtime, so it uses the phase 0
bindings.

You might have already known all that, since your question is about the
utilities submodule, but I wanted to include that explanation for
context. This module is interesting, since you require it for-syntax in
your enclosing module. This has the effect of *shifting* the phases of
your utilities submodule, so its phase 0 ends up aligning with phase 1
of the enclosing module. Now, the language of this submodule is
racket/base, which provides bindings for `provide` and `define`, but
what about the code in the template?

Well, from the utilities module’s perspective, that code is actually
going to be evaluated one phase level below phase 0: phase -1! This
phase-shifting that happens when you import things for-syntax is why
negative phases are meaningful — even though phase -1 doesn’t really
make any sense in isolation, after the phase-shifting that the
for-syntax import causes, phase -1 becomes phase 0.

Racket manages all this complicated bookkeeping behind the scenes, so
you never need to worry about which *absolute* phase your code will be
used at. What you do need to worry about is which *relative* phase
pieces of code will end up at. In your utilities submodule, the `lambda`
identifier in the template will be evaluated at relative phase level -1,
so you need to ensure racket/base’s bindings are in scope at phase level
-1. This is what for-template does: it is like for-syntax, but it shifts
imports a phase level down instead of a phase level up.

(Note that (for-template (for-syntax ....)) is a no-op, since the shifts
cancel each other out. It may be educational to think about the
implications of this for your program.)

Alexis


> On May 8, 2018, at 16:00, Greg Rosenblatt <greg.we...@gmail.com> wrote:
> 
> Hi, I'm having trouble writing a syntax transformer that uses a 
> syntax-generating procedure defined elsewhere.
> 
> When the procedure is defined locally, everything is fine.
> 
> When the procedure is defined outside the transformer, I have to do a dance 
> to make the procedure visible at the right phase, which seems to work.  
> However, upon use I get:
> 
> > racket unbound-identifier.rkt
> unbound-identifier.rkt:9:7: lambda: unbound identifier;
>  also, no #%app syntax transformer is bound
>   context...:
>    #(1973 module unbound-identifier 0) #(2181 module) #(2811 macro) #(2822 
> local)
>    #(2823 intdef) #(2824 module (unbound-identifier utilities) -1)
>   other binding...:
>    #<module-path-index:(racket)>
>    #(1972 module) #(1973 module unbound-identifier 0)
>   at: lambda
>   in: (lambda (i) (displayln (quasiquote (input: (unquote input)))))
>   context...:
>    standard-module-name-resolver
>  
> 
> I wrote this self-contained example using a submodule, but the error also 
> occurs when requiring the module from another file.  What am I doing wrong?  
> I imagine it's something silly.
> 
> 
> #lang racket
> (provide this-works this-does-not-work)
> 
> (module utilities racket/base
>   (provide compile-test)
> 
>   (define (compile-test)
>     #`(lambda (i) (displayln `(input: ,input)))))
> 
> (require (for-syntax 'utilities))
> 
> 
> (define-syntax (this-works stx)
>   (syntax-case stx ()
>     ((_ input)
>      (let ()
>        (define (compile-test)
>          #`(lambda (i) (displayln `(input: ,input))))
> 
>        #`(#,(compile-test) input)))))
> 
> (define-syntax (this-does-not-work stx)
>   (syntax-case stx ()
>     ((_ input to-do ...)
>      (let ()
> 
>        #`(#,(compile-test) input)))))
> 
> (this-works 3)
> (this-does-not-work 3)

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to