[plt-scheme] depth of macro expansion in module during module require?

From: Jens Axel Søgaard (jensaxel at soegaard.net)
Date: Wed Apr 18 10:32:11 EDT 2007

Yin-So Chen skrev:
> Hi Jens -
> 
> thanks for the quick reply.  After some more researching plus looking at 
> your code I realized where my mistakes are - I thought symbols in a 
> syntax expands out to itself, but apparently this is not the case (hence 
> my problem has nothing to do with module/macros).  Instead, I need to 
> use datum->syntax-object to make sure that timeout in syntax equals 
> timeout in symbol (BTW - thanks for your example showing below using stx 
> as the context syntax... I was scratching my head trying to figure out 
> where I can provide a context).
> 
> However, looking at your code there are some concepts that I don't 
> understand:
> 
>     * your code provides syntax name for time-out and main rather than
>       timeout, start, initial-request, and interface-version - why this
>       also works without having to convert them to syntax-object? 

Section 12.3.5 "Macro-Generated Top-Level and Module Definitions"
contains the explanation.

<http://download.plt-scheme.org/doc/360/html/mzscheme/mzscheme-Z-H-12.html#node_sec_12.3.5>

   In provide (see section 5.2), for a provide-spec of the form
   identifier, the exported identifier is the one that binds identifier
   within the module in a generator-specific way, but the external name
   is the plain identifier.
   ...
   For all-defined and all-defined-except, only identifiers with
   definitions having the same generator as the all-defined or
   all-defined-except keyword are exported; the external name is the
   plain identifier from the definition. The generator of an all-from or
   all-from-except provide-spec does not affect the set identifiers
   exported by the provide-spec.

So (provide (all-defined)) will only provide names defined by the *same 
macro expansion*. That's why it is convenient to expand into

   (begin
    (define name 'foo)
    (provide foo))

if one want to make sure name is to be provided.

>     * how does the nested define-syntax work both in terms of expansion
>       and scoping? 

In my version

        (servlet
          (time-out +inf.0)
          (main `(p "hello world")))

is first expanded into

(begin
   (define-syntax (time-out stx) ...)
   (define-syntax (main stx) ...)
   (time-out +inf.0)
   (main `(p "hello world"))
   (define interface-version 'v1)
   (provide (all-defined)))

Then this is expanded. Since the (syntax) definition of main precedes
the macro use (main ...), there are no scope problems.

>     * Aesthetics and styles aside - is there a reason for nested
>       define-syntax?  (It looks cool to learn, but I came from the C
>       world where definitions belong on top level, so want to know why
>       and how I can think in this fashion)

An alternative is to use a helper module:

  (module syntax-helper mzscheme
     (define-syntax (time-out stx) ...)
     (define-syntax (main stx) ...)
     (provide (all-defined)))

and then use

   (define-syntax servlet
     (lambda (stx)
       (syntax-case stx ()
         ((_ body ...)
          (with-syntax ([time-out (datum->syntax-object stx 'time-out)]
                        [main     (datum->syntax-object stx 'main)])
               #`(begin
                    (require "syntax-helper.scm")
                     body ...
                     (define interface-version 'v1)
                     (provide (all-defined))))))))

[At least I think so, this time I didn't test]


-- 
Jens Axel Søgaard



Posted on the users mailing list.