[racket] On Cooperating Macros

From: Ray Racine (ray.racine at gmail.com)
Date: Fri Nov 16 18:46:49 EST 2012

Thanks for answering on a Friday night.   I'll limit my questions to one
and do some more homework now that you've reduced the search space.

Regarding,

"The *only* way for module A to affect the compilation of module B in any
way is for module B to require (directly or indirectly) module A.

And the only way to make a compile-time side-effect in module A
"persistent", so it will affect the compile-time state of other module
compilations is to put it in a 'begin-for-syntax'."

True or False, anything you can do with begin-for-syntax can be done via a
module, providing and subsequently requiring where needed via:
(require (for-syntax ...))
(require (only-meta-in ...))
(require (for-meta 1 ...))

Thanks in advance,

Ray


On Fri, Nov 16, 2012 at 6:05 PM, Ryan Culpepper <ryan at cs.utah.edu> wrote:

> On 11/16/2012 05:16 PM, Ray Racine wrote:
>
>> I've run across a few things mentioning cooperating macros, something
>> I'm working through.
>>
>> Consider the following in a source file "nest.rkt"
>> https://gist.github.com/**4090877 <https://gist.github.com/4090877>
>>
>> So in this example each expansion of the two defined macros in the
>> `nest' module sees mutually mutable state at meta level 1.
>>
>> And of course in a separate source module, say "bird-watcher.rkt" with
>> the following.
>>
>> #lang racket
>>
>> (require "nest.rkt")
>>
>> (define the-nest
>>     (begin
>>      (daddy-brings-worms)
>>      (daddy-brings-worms)))
>>
>> the-nest
>>
>> ;; end-of-module
>>
>> One sees that the mutable state is not maintained across modules.  i.e.,
>> Not cooperating.  All, I'm guessing, a direct consequence of the
>> "Immutable Laws Of Flatt" (ILOF), "You Want It When", and "Modules,
>> Visitations and Instantiations, Oh My".
>>
>
> Yes. To summarize: the expansion/compilation of each module starts with a
> fresh state, which is then initialized with the compile-time parts of the
> modules it depends on.
>
>
>  BUT, the ILOF has a backdoor[1], there exists a meta level wormhole
>> which allows state to tunnel through modules and expansions.  If one
>> were to use `define-syntax' at compile time (during meta level 1) to
>> bind a non-procedure value, one can rat-hole state which is subsequently
>> accessible via `syntax-local-value' in an a different macro expansion at
>> meta level 1, in a different module. i.e., the macros now can cooperate.
>>   Here I'm taking "cooperate" as synonymous with shared mutable state.
>>
>> Based on what I think I understand, I can rejigger my bird net examples
>> to cooperate using the wormhole.
>>
>
> No, that's not a back door, because you still get a fresh state and then
> you execute the definition again. So if you did something like
>
>   (define-syntax the-nest-state (box (cons 0 0)))
>
> and then mutated the box during the expansion of module A, then when you
> start expanding module B you execute that syntax definition again, so
> module B starts out with a fresh box holding (cons 0 0).
>
> --
>
> The *only* way for module A to affect the compilation of module B in any
> way is for module B to require (directly or indirectly) module A.
>
> And the only way to make a compile-time side-effect in module A
> "persistent", so it will affect the compile-time state of other module
> compilations is to put it in a 'begin-for-syntax'. (Actually, there are a
> few other ways, but they take longer to explain.)
>
> --
>
> I'll stop here in case you want to reformulate the questions in your next
> few paragraphs based on my answers so far.
>
> Ryan
>
>
>  So assuming the above is more-or-less correct, can cooperating macros be
>> accomplished solely via the application of module require / provide at
>> proper meta levels.  One one hand, I'm visualizing a `define-syntax'
>> binding a non-procedure value at compile time (meta 1) as in essence
>> pushing the value up into meta 2.  And the `syntax-local-value' as
>> reaching out from meta 1 into meta 2 to fetch the state down.  It's
>> global because meta layer 2 is a single global environment in which meta
>> 1 syntax transformers are evaluated.
>>
>> On the other, other hand I visualize that during module visitations and
>> instantiations the _only_ thing which can _cross_ meta levels is syntax.
>> i.e. syntax at meta level N can be lifted into meta level N+1,
>> transformed and returned back to meta level N.  But as only syntax
>> crosses meta boundaries, hence the define-syntax and syntax-local-value
>> backdoor.
>>
>> Or another way of phrasing the question, "Can one
>> write cooperating macros without resorting to the use of `define-syntax'
>> and `syntax-local-value'".   If yes, how?
>>
>>
>> Thanks,
>>
>>
>> Ray
>>
>>
>> [1] Seems the only true immutable law in CS is, there's always a backdoor.
>>
>>
>> ____________________
>>    Racket Users list:
>>    http://lists.racket-lang.org/**users<http://lists.racket-lang.org/users>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20121116/cea8a115/attachment.html>

Posted on the users mailing list.