[racket] selectively local-expand sub-expressions

From: Carl Eastlund (carl.eastlund at gmail.com)
Date: Sat Jan 25 21:00:01 EST 2014

Scott,

I see what you're doing now.  You're not actually trying to use macro
expansion at all; you're just using local-expand to substitute the
definition of pred? where it occurs, so that you can make its macro
definition also serve as its DSL definition.  That's sensible, but
local-expand is still doing more than you want it to.  That's why I put in
all the expansion caveats -- not because you necessarily meant to do full
expansion, but because local-expand is pretty explicitly built for full
expansion, and always tries to push as far as it can.  Any time the caveats
about expansion don't apply, local-expand is probably a bigger gun than you
need.

Where local-expand is going to bite you is when the definition of pred?
uses a macro at its top level.  For instance:

  (define-syntax-rule (pred? x) (or (< x 3) (> x 7)))

Here, local-expand is going to expand the use of (or ...), and any macro
that (or ...) produces at its top level, until you reach a core form as the
main expression, or something you've put in an explicit stop list.  That's
not what you want, as I understand it -- you only want to expand pred?.

So what to do when you want to apply one macro, but not perform general
expansion?  Extract its transformer using syntax-local-value, and apply it
to the expression.  You probably also want to apply a syntax mark before
and after transformation, just to simulate the base level of hygiene the
macro may be relying on.  It might not be necessary for simple definitions,
but it can't hurt.

I wrote up some code that does this, along with a test showing that it
won't expand "or" too far.  It's also reasonably hygienic -- it won't be
confused if someone defines a different macro named "pred?", for example.
I don't know if that's a concern, but again, it can't hurt.  Anyway, you
can find what I wrote here: https://gist.github.com/carl-eastlund/8626893

Carl Eastlund

On Fri, Jan 24, 2014 at 1:30 PM, Scott Klarenbach <scott at pointyhat.ca>wrote:

> Just an update, I was able to make this work.
>
> #lang racket
> (require (for-syntax racket/syntax syntax/stx))
>
> (define-syntax-rule (pred? x) (> 3 x))
>
> (define-for-syntax (recursive-expand stx)
>   (let loop ([l (syntax->list stx)])
> (cond [(stx-null? l) l]
>   [(stx-pair? (stx-car l))
>    (cons (loop (stx-car l)) (loop (stx-cdr l)))]
>   [(equal? 'pred? (syntax->datum (stx-car l)))
>    (local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression #f)]
> ;; this works
>   [else
>    (cons (stx-car l) (loop (stx-cdr l)))])))
>
> (define-syntax (test stx)
>   (syntax-case stx ()
> [(_ x)
>  (with-syntax ([expanded (recursive-expand #'x)])
>    #''expanded)]))
>
> (module+ test
>   (require rackunit)
>   (check-equal? (test (or (< 10 x) (pred? y)))
> '(or (< 10 x) (> 3 y))))
>
> The code I couldn't figure out last night was:
> (local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression #f)]
>
> Thanks.
>
> --
> Talk to you soon,
>
> Scott Klarenbach
>
> PointyHat Software Corp.
> www.pointyhat.ca
> p 604-568-4280
> e scott at pointyhat.ca
> 200-1575 W. Georgia
> Vancouver, BC V6G2V3
>
> _______________________________________
> To iterate is human; to recur, divine
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140125/d450f01e/attachment.html>

Posted on the users mailing list.