[plt-scheme] Using previously-defined macros within the "transformer environment"

From: Matthew Flatt (mflatt at cs.utah.edu)
Date: Fri Sep 12 21:18:54 EDT 2008

At Fri, 12 Sep 2008 08:28:28 -0700 (PDT), "///" wrote:
> I'm using PLT Scheme to write a Web site. In order to make PLT Scheme
> more usable (that is, more like Common Lisp), I've been spending some
> time writing a few macros. First, I wrote a new version of the
> "lambda" form. It's just like the original lambda, except it
> understands &OPTIONAL, &KEY, and &REST, like Common Lisp (&KEY is
> especially important). It was a first step in porting some of my CL
> code over to Scheme.
> 
> Next, I decided to try to write a DEFMACRO form that understands the
> same sort of lambda-list. (PLT is generous enough to provide "define-
> macro", but macros defined this way can only have Scheme lambda-lists,
> and the macros I'm trying to port rely heavily on &KEY-- a simple dot
> will not do!) The easiest way to do this would be to use the LAMBDA
> macro I previously wrote, so that the macro-expansion invokes the
> LAMBDA macro and automatically inherits its lambda-list processing
> capabilities. Unfortunately, it turns out that my LAMBDA macro is not
> visible in the transformer environment, even though I used (require-
> for-syntax cl-lambda) to import the module that defines it. I would
> hate to have to copy and paste the source for the LAMBDA form (and all
> the local functions it depends on) to get my DEFMACRO form to work,
> but at the moment it looks like that's what you have to do to satisfy
> the Hygienic Macro System. I tried to solve the problem with Google
> (which has helped on countless occasions), but apparently nobody has
> ever tried to use one macro to expand another in Scheme.
> 
> Here's what the defmacro macro looks like:
> 
> (define-macro (defmacro name lambda-list . body)
> 	  (let* ((rest-arg (gensym))
> 		 (simple-lambda-list (make-simple-lambda-list
> 					    (fold-optional-args-into-rest 
> lambda-list rest-arg))))
> 	    `(define-macro ,(cons name simple-lambda-list)
> 	       (apply (cl-lambda ,lambda-list , at body)
> 		      ,(cons-to-rest-arg-if-necessary simple-lambda-list rest-
> arg)))))
> 
> "simple-lambda-list" converts a CL lambda-list into a Scheme one
> (including that
> stupid little dot), while "fold-optional-args-into-rest" removes all
> &OPTIONAL and
> &KEY arguments and adds a &REST argument to replace them (and also
> replaces
> any user-provided &REST arguments with its own definition).
> (foo bar &optional baz &rest haha) becomes (foo bar &rest g39).
> 
> It expands as expected: (defmacro foobar (x &optional y)
> `(list ,x ,y)) expands into:
> 
> (define-macro (foobar x . g73)
>   (apply (cl-lambda (x &optional y) (quasiquote (list (unquote x)
> (unquote y))))
> 	 (cons x g73)))
> 
> However, the above macro can't be expanded any further because:
> 
>   reference to undefined identifier: cl-lambda
> 
> I know the problem is caused by the transformer environment because
> the cl-lambda form works from the REPL. In fact, if you define x and
> g73 in a let form and paste in the definition of the above macro, you
> get the expansion that should result from the macro:
> 
> (let ((x 1)
>       (g73 '(2)))
>     (apply (cl-lambda (x &optional y) (quasiquote (list (unquote x)
> (unquote y))))
> 	 (cons x g73)))
> 
> ==> (list 1 2)
> 
> define-for-syntax allows you to define functions in the transformer
> environment, but there is no such thing as define-macro-for-syntax, or
> even define-syntax-for-syntax. The PLT Scheme reference manual is
> absolutely silent on this issue, except that it seems to suggest
> (incorrectly) that require-for-syntax should solve the problem (it
> only helps with functions). 


`require-for-syntax' really is the solution for macros as well as
functions.


> Has anyone ever done this sort of thing in
> Scheme before? 

A lot, especially in PLT Scheme.


> Is it fixed in a later version of PLT Scheme?
> 
> My ISP is using MzScheme 360.

Version 4.1 offers many improvements, including better documentation.
In particular, the following part of the current PLT Scheme Guide
offers an explanation of the problem that you're hitting, along with
its solution:

  http://docs.plt-scheme.org/guide/stx-phases.html

In that part of the Guide, the example is a `check-ids' function, but
the same line of reasoning applies to a macro like `cl-lambda'.

The essential part of the solution is to put the revised `cl-lambda'
macro in a module, and then you can use it in run-time expressions by
`require'ing the module, and you can use it in compile-time expressions
by using `require' with `for-syntax'.

It's the same in v360, except that `require' with a `for-syntax'
sub-form is written as a `require-for-syntax' form.


Matthew



Posted on the users mailing list.