[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: structures at expansion time
Quoting Ryan Culpepper:
> I'm trying to write a library which stores expansion-time information
> in identifiers (like signatures contain a vector of symbols) for
> access by several different modules. There is one core module which
> provides syntax for building information. The core module is required
> by several others which use this syntax. Since the information is
> somewhat complex, I was trying to store it in structures. The
> structure definitions are in another module require-for-syntax'd by
> the core module.
You might mean either of two things:
1. your macro generates in instance of the structure, or
2. your macro generates an expression that instantiates the structure.
#1 doesn't work. #2 does.
Here's a program to illustrate #1:
--------------------
(module d mzscheme
(provide (struct d (x)))
(define-struct d (x)))
(module s mzscheme
(provide s)
(require-for-syntax d)
(define-syntax (s stx)
(syntax-case stx ()
[(_ var)
(with-syntax ([val (make-d 10)])
(syntax (define-syntax var val)))])))
(module a mzscheme
(require s)
(provide instance)
(s instance))
(module b mzscheme
(require a)
(require-for-syntax d)
(t instance)
(define-syntax (t stx)
(syntax-case stx ()
[(_ var)
(let ([val (syntax-local-value (syntax var))])
(printf "instance?: ~a~n" (d? val))
#'(void))])))
--------------------
The above program prints "instance?: #f".
But by changing line 13 to
(with-syntax ([val (syntax (make-d 10))])
^^^^^^^ ^
we get "instance?: #t".
I'm guessing that you have a program like the original one, and you want
a program like the revised one.
The difference is that the original program produces "three
dimensional" code; a use of `s' expands to something with no textual
analogue:
(define instance #<stuct:d>)
The revised code expands instead to
(define instance (make-d 10))
and that's essentially why it works.
A good argument could be made for signalling an error when a program
attempts to produce 3-D syntax objects. But PLT Scheme doesn't signal
an error because 3-D syntax can be handy on occassion. (For example,
errortrace performs a 3-D transformation on syntax.)
> However, it seems that the structure definitions are re-evaluated
> every time the core module is required, so information built in one
> module cannot be accessed by another (because the accessors don't
> match the right structure type).
It's not every time the module is required; but a required-for-syntax
module is invoked every time is is used to expand a different module.
Thus, in the example above, `s' is invoked once to expand `a', and
again to expand `b'. The revised example works because the
expansion-time portion of `a' is also executed afresh when `b' is
expanded (so the `make-d' call happens again as `b' as expanded, not
just once as `a' is compiled).
> While I think I understand why the module system works this way (to
> make separate compilation consistant?),
Right. It's all designed to make you post to plt-scheme now, instead of
much later when you're trying to push the code through mzc. :)
(The plt-scheme post, in turn, helps me figure out how to better
explain and document things.)
> it seems to imply that
> expansion-time information (to be accessed by syntax-local-value)
> cannot usefully contain anything but built-in data types.
> [...]
> Is there any way to get this to work, or do I need to take a
> different approach?
If I'm guessing correctly, you'll probably be able to fix your code
easily.
`define-signature' and `unit/sig' rely on a structure for signature
information. The same is true of EoPL's `define-datatype', which
cooperates with `cases'.
In fact, even `syntax-case' and `syntax' are implemented as macros, and
there's a private struct type for pattern-varable bindings. A
`syntax-case' expression
(syntax-case stx ()
[a (syntax a)])
expands to something like
(let ([gensymed1 stx])
(let-syntax ([a (make-pattern-var #'gensymed1)])
(syntax a)))
where the `syntax' macro detects that `a' is bound at expansion time to
an instance of `pattern-var', and the `pattern-var' instance indicates
that the run-time value for `a' will be stored in `gensymed1'.
Matthew