[racket] Macro design question: best way to interpret an expression multiple ways

From: Neil Toronto (neil.toronto at gmail.com)
Date: Fri Sep 6 12:38:59 EDT 2013

Interesting! I'm already using syntax classes and attributes to factor 
interpreting primitive functions out of the main `interp' macro, too...

I realized yesterday that I don't necessarily want the code to always 
expand to uses of `&&&' and `>>>'. There may be other interpretations I 
want to do that can't or shouldn't expand to arrow computations. (For 
example, if I don't expand to arrows, I might be able to make an 
interpretation that preserves user type annotations.) So I've settled on 
expanding multiple times, using a variant of Carl's #2, but with a 
syntax parameter that holds a dispatch table that associates ids with 
syntax transformers. If I do this:

    (define-dispatcher-parameter drbayes-dispatcher)

    (define-parameterized-syntax &&& drbayes-dispatcher)
    (define-syntax (&&&/bot stx) ....)
    (define-syntax (&&&/pre stx) ....)

    (dispatcher-set! bot-dispatcher &&& &&&/bot)
    (dispatcher-set! pre-dispatcher &&& &&&/pre)

    .... other combinators ....

Then how `&&&' expands depends on the value of `drbayes-dispatcher'; 
i.e. these two are different interpretations:

    (syntax-parameterize ([drbayes-dispatcher  bot-dispatcher])
      (((const 4) . &&& . (const 5)) . >>> . add))

    (syntax-parameterize ([drbayes-dispatcher  pre-dispatcher])
      (((const 4) . &&& . (const 5)) . >>> . add))

Parameterized syntax like `&&&' just looks up its implementation using 
its given id. If I want to have an interpretation do something wildly 
different, I can make `interp' into parameterized syntax.

The `interp' macro I already had, which had expanded to uses of the 
super-combinators, barely had to change.

I'm sure there's some cute way to do this using units, but I don't 
currently have the time to try it.

Neil ⊥

On 09/05/2013 06:45 AM, Jay McCarthy wrote:
> 4. Use syntax-parse with attributes that do both expansions at the same
> time. The automata library does this:
> https://github.com/plt/racket/blob/master/pkgs/unstable-pkgs/unstable-lib/automata/re-compile.rkt
>
>
>
>
> On Wed, Sep 4, 2013 at 3:40 PM, Carl Eastlund <cce at ccs.neu.edu
> <mailto:cce at ccs.neu.edu>> wrote:
>
>     I see three options, off the top of my head:
>
>     1. Expand into a lambda that takes the different functions, such as
>      >>> and &&&, as arguments; then apply that function in one place to
>     the /pre variants and in another place to the /bot variants.
>
>     2. Expand into one expression, and make &&& and >>> into parameter
>     values; set them differently on two different calls.
>
>     3. If &&& and >>> need to be macros themselves for some reason, then
>     you may really have two different interpretations here that must be
>     expanded separately.  Sometimes you just have to do work twice.
>     Although you may be able to save yourself some of that with
>     judicious local-expansion... but that's probably more trouble than
>     it's worth.  Also, unless "interp" is likely to be nested, simply
>     expanding twice is unlikely to cause a significant problem.
>     Sometimes it's okay to have a single, limited source of macro
>     duplication.
>
>     Carl Eastlund
>
>
>     On Wed, Sep 4, 2013 at 5:31 PM, Neil Toronto <neil.toronto at gmail.com
>     <mailto:neil.toronto at gmail.com>> wrote:
>
>         I have two libraries of combinators meant to be used as targets
>         for a language semantics. I'm implementing the semantics using
>         macros; e.g.
>
>             #'(interp (+ 4 5))  =>  #'(((const 4) . &&& . (const 5)) .
>          >>> . add)
>
>         where `interp' is a macro and `=>' means macro expansion.
>
>         Here's the tricky part. I have to interpret the language using
>         *both* combinator libraries, not just one; e.g. I need #'(interp
>         (+ 4 5)) to expand to both of these:
>
>            #'(((const/bot 4) . &&&/bot . (const/bot 5)) . >>>/bot . add/bot)
>            #'(((const/pre 4) . &&&/pre . (const/pre 5)) . >>>/pre . add/pre)
>
>         I could have #'(interp (+ 4 5)) expand to something like
>
>            #'(list (interp/bot (+ 4 5)) (interp/pre (+ 4 5)))
>
>         But expanding twice is terrible manners when the expression
>         contains macros.
>
>         (Originally, I had super-combinators that did the job of both
>         */bot and */pre combinators. These super-combinators were
>         complicated and messy, especially when used in recursion, which
>         is why I'm moving the "interpret the language two ways" job into
>         the macro system and hoping it's nicer. It should also generate
>         faster code, and possibly allow type checking on one of the
>         interpretations.)
>
>         The best idea I've had so far is to have (interp e) expand to
>         uses of generic `&&&' and `>>>' combinators, apply
>         `local-expand', and do a syntax tree search-and-replace that
>         does #'&&& => #'&&&/bot, etc. Is there another way, though, that
>         doesn't run afoul of macro security?
>
>         Neil ⊥
>         ____________________
>           Racket Users list:
>         http://lists.racket-lang.org/__users
>         <http://lists.racket-lang.org/users>
>
>
>
>     ____________________
>        Racket Users list:
>     http://lists.racket-lang.org/users
>
>
>
>
> --
> Jay McCarthy <jay at cs.byu.edu <mailto:jay at cs.byu.edu>>
> Assistant Professor / Brigham Young University
> http://faculty.cs.byu.edu/~jay
>
> "The glory of God is Intelligence" - D&C 93


Posted on the users mailing list.