[racket] Macro design question: best way to interpret an expression multiple ways
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