[plt-scheme] Let-syntax not transparent to top-level defines
Many thanks for your response! I'm still figuring out how the LOCAL-EXPAND stuff works...
--- Matthew Flatt wrote:
I'm a little confused about the setup. `(with-tracing (+ 1 2))'
presumably doesn't expand to `(let-syntax ([#%app ...]) (+ 1 2))'. The
`#%app' binding wouldn't affect the `(+ 1 2)' part, since the binding
is introduced by the macro, right? Maybe you're using
`fluid-let-syntax'? Or maybe something else is going on and the details
don't matter.
--- end of quote ---
It's actually a pretty simple macro, modulo one hygiene violation to work around the problem you describe with #%app:
;; The goal: With a with-tracing form, replace (#%app name . values)
;; with (#%app trace-call 'name name . values). We do this using
;; let-syntax, which imposes certain limitations--namely, we can't
;; wrap with-tracing around a top-level defining form.
;;
;; We use #,(datum->syntax-object stx '#%app) instead of simply #%app
;; to name the syntax we're overriding, because if we don't, nothing
;; happens.
(define-syntax (with-tracing stx)
(syntax-case stx []
[(with-tracing body ...)
(quasisyntax/loc
stx
(let-syntax [[#,(datum->syntax-object stx '#%app)
(syntax-rules []
[(_ name . values)
(#%app trace-call 'name name . values)])]]
body ...))]))
The problem arises because LET-SYNTAX does not preserve the "top-levelness" property of the code in "body". (Otherwise, the sematics are *perfect*--LET-SYNTAX doesn't catch #%app forms introduced by macro-expansion the way FLUID-LET-SYNTAX would, which would bury users in debug information.)
What I wish were happening:
* LET-SYNTAX would continue to behave the way it does now, for all the reasons you've mentioned.
* A new form, BEGIN/LET-SYNTAX (or whatever) would introduce a local scoped binding--not an internal definition--but it would affect the definition context of its body in precisely the same way as BEGIN.
In other words:
(begin/let-syntax [[foo ...]]
(define (x) (foo))
(define (y) (foo)))
...should produce two global definitions, but this:
(define (bar)
(begin/let-syntax [[foo ...]]
(define (x) (foo))
(x))
; foo is not visible here.
)
...should behave the way BEGIN does in a body.
This would probably mean implementing something like BEGIN/LETREC-SYNTAXES+VALUES, which might range from trivial to insanely difficult, given the compiler's architecture.
Does this even make sense? If so, is it remotely feasible? If it's feasible, is it actually a good idea?
I'm going to study the example code you posted, and see if there's any way I can use that information to implement BEGIN/LET-SYNTAX. Many, many thanks for your help!
Cheers,
Eric
(who's perfectly prepared to hear that he's a raving nutcase for even asking these questions)