I'm stretching details a bit, but maybe it would help to think of
phases as "passes." That is, there's a compile-time "pass" where
Racket is focuses on the code you write and what they can see.
These passes continue until the Racket program is fully expanded.
Where things get tricky is remembering that when you create a
binding at one phase, it is available for that phase. It's
a little easier to tell the difference across module boundaries.
Let's say you have a library that defines your functions at phase
0, or runtime.
; lib.rkt
#lang racket/base
(provide (all-defined-out))
(define (my-function x) (+ x 1))
(define (my-macro stx)
(datum->syntax stx (my-function
(cadr (syntax->datum stx)))))
Now let's have another module use the syntax transformer. I'm
handwaving around some details because `define-syntax` and
`define-for-syntax` are not the same, but I'd like to focus on
illustrating how phases operate.
#lang racket/base
(require "lib.rkt")
(my-macro #'(x 1))
The reason this works is because all the bindings are in the
same phase. That is, everything was defined in the same pass. Now
let's shift it all one phase up, which will break the program
because it no longer sees my-macro, or even enough of racket/base
to apply functions.
#lang racket/base
(require (for-syntax "lib.rkt"))
(my-macro #'(x 1))
Right now `my-macro` is in phase 1 relative to this module.
So we have to "lift" the rest of the code to match.
#lang racket/base
(require (for-syntax racket/base
"lib.rkt"))
(begin-for-syntax (my-macro #'(x 1)))
This still isn't particularly useful because most of the
time, a module manages multiple phases at once. It can be harder
to visualize, but the principle is the same: When code runs at a
certain phase, is everything that code needs to run also
available at that phase? It's still just Racket. I like
to visualize it as running at a different "layer" on top of the
code that I know will eventually execute at runtime. Here's
another example that can help drive the point home. Run it using
the `racket` command.
#lang racket/base
(require (for-syntax racket/base))
(define a "foo")
(define-for-syntax a 1)
(displayln a)
(begin-for-syntax (displayln a))
Notice that you see "1" first, before "foo", even though the
displayln for the "1" is after the displayln for "foo".
So to answer your question, if you have something you want
available across phases, you need to bind that same
value across phases. Here's a simplified example.
#lang racket/base
(require (for-syntax racket/base))
(define-syntax-rule (define-across-phases id v)
(begin (define id v)
(define-for-syntax id v)))
(define-across-phases a 1)
(displayln a)
(begin-for-syntax (displayln a))
Notice that I leverage a phase to define an identifier twice:
Once for the current phase, and once for the phase +1 "layer"
up.
But... I normally do bind across phases using (require) with
both for-syntax and without a phase shift. e.g. (require
"lib.rkt" (for-syntax "lib.rkt")). There are times I'll need
cross-phase definitions only within one module, but it doesn't
come up much for me.
Hope this helps.
On 5/9/21 3:53 AM, Yushuo Xiao wrote:
I am using syntax transformers to define macros in Racket. I
want to create some helper functions to help me manipulate the
syntax. However, the functions I defined outside the syntax
transformer are not available inside the syntax transformer. For
example, in the following code
(define (my-function x) (+ x 1))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx
(my-function (cadr (syntax->datum stx))))))
I got the error "my-function: reference to an unbound
identifier at phase: 1; the transformer environment".
After some searching, I am able to write the following code
so that `my-function` is available inside the syntax
transformer.
(begin-for-syntax
(define (my-function x) (+ x 1)))
(provide (for-syntax my-function))
(define-syntax my-macro
(lambda (stx)
(datum->syntax stx
(my-function (cadr (syntax->datum stx))))))
But the problem is, `my-function` is not available outside
the syntax transformer this time. Sometimes I want to check
those helper functions in ordinary code, so I need to be able to
call it from both inside and outside the syntax transformer,
just like the function `cadr`. How can I achieve that?
I know my question has something to do with Racket's syntax
model, in particular the concept of "phase level", but I never
really understand it. If you could provide some easy-to-follow
tutorials explaining it I would even be more grateful.
--
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...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/46cce5b2-251b-481c-afe2-28582e8c44f3n%40googlegroups.com.
--
~slg