[plt-scheme] Injecting syntax into load

From: Danny Yoo (dyoo at hkn.eecs.berkeley.edu)
Date: Thu Dec 7 13:16:58 EST 2006


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!

Posted on the users mailing list.