[plt-scheme] Injecting syntax into load
On Thu, 7 Dec 2006, Norman Gray wrote:
> Can anyone tell me how I would inject new syntax into the syntax transformer
> used by LOAD, in such a way that REQUIRE in the loaded file works?
Hi Norman,
Here's an example that might help:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module test mzscheme
(parameterize ([current-namespace (make-namespace)])
(namespace-transformer-require 'mzscheme)
(eval
' (begin
(define-syntax (double stx)
(syntax-case stx ()
[(_ x)
(syntax/loc stx
(list x x))]))))
(let loop ([s-expr (read)])
(cond [(eof-object? s-expr) 'done]
[else
(display (eval s-expr))
(newline)
(loop (read))]))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Namespaces affect EVAL, so whenever we want to do something with them, we
either need to use the namespace-specific functions, or EVAL/LOAD.
The code above injects a new macro into the newly constructed namespace,
after which we can play around with it using the hacky repl there.
(There's some issue with NAMESPACE-TRANSFORMER-REQUIRE that I don't quite
understand fully, which I'll mention near the bottom.)
> but that doesn't work if the loaded file has module requirements (or
> even if it has definitions following expressions), since these can only
> appear at the top level.
Another example of this can be found in the code for mred's
MAKE-NAMESPACE-WITH-MRED:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define make-namespace-with-mred
(opt-lambda ([flag 'mred])
(unless (memq flag '(initial mred empty))
(raise-type-error 'make-namespace-with-mred
"flag symbol, one of 'mred, 'initial, or 'empty"
flag))
(let ([orig (current-namespace)]
[ns (make-namespace (if (eq? flag 'empty) 'empty 'initial))])
(parameterize ([current-namespace ns])
(namespace-attach-module orig mred-module-name)
(when (eq? flag 'mred)
(namespace-require mred-module-name)
(namespace-require class-module-name)))
ns)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
So notice that they're using namespace-require. You could probably do the
same. So, concretely:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module test mzscheme
(require (lib "mred.ss" "mred"))
(define (test1)
(parameterize ([current-namespace (make-namespace-with-mred)])
(eval '(new object%))))
(define (test2)
(parameterize ([current-namespace (make-namespace)])
(eval '(new object%)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
TEST1 succeeds, because it introduces macros for OOP stuff via the
enriched namespace, but TEST2 fails.
There's some parts about the system that I'm still very wishy-washy about.
For example, the documentation for make-namespace says:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
* 'initial (the default) -- the new namespace contains the module
declarations of the initial namespace (see section 8.2), and the new
namespace's normal top-level environment contains bindings and imports as
in the initial namespace. However, the namespace's transformer top-level
environment is empty.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I see that the last part, about the namespace's transformer top-level
environment being empty, is true, since in the very first example, if I
omit the call to NAMESPACE-TRANSFORMER-REQUIRE, the macro definition
breaks.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
. compile: bad syntax; function application is not allowed, because no
#%app syntax transformer is bound in: (syntax-case stx () ((_ x)
(syntax/loc stx (list x x))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I'm just not quite sure yet why the transformer environment should be
empty, or if there is something deeper here that I'm missing.
Best of wishes!