[racket] local-expand and stop-lists?

From: Ryan Culpepper (ryan at cs.utah.edu)
Date: Fri Aug 12 14:27:42 EDT 2011

On 08/12/2011 12:08 PM, Sam Tobin-Hochstadt wrote:
> On Fri, Aug 12, 2011 at 1:58 PM, Ryan Culpepper<ryan at cs.utah.edu>  wrote:
>> On 08/12/2011 11:37 AM, Sam Tobin-Hochstadt wrote:
>>>
>>> On Fri, Aug 12, 2011 at 1:25 PM, Danny Yoo<dyoo at cs.wpi.edu>    wrote:
>>>>
>>>> In which the first three lines are coming from compile-time, and I see
>>>> that my lift-to-toplevel macro is firing off, even though I placed it
>>>> in the stop-list of local-expand.
>>>
>>> What's happening here is that the `#%module-begin' binding from
>>> `racket/base' calls `local-expand' on each of its forms, to determine
>>> whether its an expression or not.  This implements the printing of
>>> top-level expressions.  If you replace `#%module-begin' with
>>> `#%plain-module-begin', you should see the desired behavior.
>>
>> '#%plain-module-begin' also calls 'local-expand' to expose definitions,
>> requires, etc. When I change Danny's sample code to use
>> '#%plain-module-begin' instead of '#%module-begin', I get the same output.
>
> `#%plain-module-begin' doesn't seem to explicitly call `local-expand',
> in the sense that the Macro Stepper shows.  For example, this program:
>
> (module m racket
>    (#%plain-module-begin (#%expression 3) 4))
>
> when macro-stepped, shows no local-expand steps, but this program:
>
> (module m racket
>    (#%module-begin (#%expression 3) 4))
>
> does show local-expansion steps.

When a primitive form like '#%plain-module-begin' performs partial local 
expansion, the macro stepper shows it as expansion in a context, because 
it knows that primitive forms only expand sub-forms, only expand them 
once, etc.

>> In general, when a 'local-expand' happens inside of another 'local-expand',
>> the outer stop list is discarded. Since '#%plain-module-begin' effectively
>> calls 'local-expand', I would expect the #'lift-to-toplevel stop list to
>> never have any effect.
>
> That's not quite true.  This program shows the difference between
> having it in the stop-list and not:
>
> (module e 'small-lang
>    (printf "hello world\n")
>    (#%expression (lift-to-toplevel (printf "ok!"))))
>
> In general, if `lift-to-toplevel' is an expression form, then I'd do
>
> (define-syntax-rule (lift-to-toplevel-aux . rest)
>    (#%expression (lift-to-toplevel . rest)))
>
> Which I think would fix the problem (even for `racket/base's `#%module-begin').
>
> If it needs to be a top-level form, more tricks are needed.

Ah, that make sense. In pass 1 (uncover definitions), there is a stop 
list that includes all primitive forms. In pass 2 (expand expressions), 
there is no stop list, so the outer stop list should be in effect again. 
So a use of 'lift-to-toplevel' only has to survive pass 1.

Of course, if 'lift-to-toplevel' may contain definitions or require 
forms, hiding it from pass 1 by pretending it's an expression may cause 
the rest of the module's expansion to go wrong. I assume that's what you 
meant by "if it needs to be a top-level form".

Ryan


Posted on the users mailing list.