[racket] selectively local-expand sub-expressions

From: Carl Eastlund (carl.eastlund at gmail.com)
Date: Sun Jan 26 18:34:16 EST 2014

You can do it like this:

  https://gist.github.com/carl-eastlund/8640925

Basically, you quasiquote the result of the expansion, and then you unquote
variables when you hit them so that they are evaluated normally.  Anything
with an unbound variable will fail at expansion time.

Carl Eastlund


On Sun, Jan 26, 2014 at 6:02 PM, Scott Klarenbach <scott at pointyhat.ca>wrote:

> Sorry, I was combining all the examples into one for brevity.  x has 2
> possible bindings (one in the module and one in the macro), y is bound in
> the module and closes over the macro, and z is unbound and therefore should
> result in an error since nothing can be done with it.
>
> ;; example 1 (as per our earlier discussion)
> (define-dsl-syntax (pred? x) (or (= 2 x) (> x 3)))
>  (recursive-expand (pred? 1))
> '(or (= 2 1) (> 1 3))
>
> ;; example 2 (capturing a reference bound by the enclosing environment)
> (define y 3)
> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))
> (recursive-expand (pred? 1))
> '(or (= 2 1) (> 1 3))
>
> ;; example 3 (ensuring some degree of hygiene, note the two bindings for x)
> (define x 3)
> (define y 3)
> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))
> (recursive-expand (pred? 1))
> '(or (= 2 1) (> 1 3))
>
> ;; example 4, (I'll throw in this case, or replace the quoted output with
> ??? for each unbound id
> (define-dsl-syntax (pred? x) (or (= 2 x) (> x z)))
> (recursive-expand (pred? 1))
> >> unbound identifier: z at pred? ;; or maybe
> '(or (= 2 1) (> 1 ???))
>
> Just to rephrase, my "expansion" is essentially just a "procedure->string"
> operation, or a custom-write, that I will use to target sql or something
> else.  Any use of pred? at runtime within racket will run fine as is.
>
> Thanks,
> Scott.
>
>
> On Sun, Jan 26, 2014 at 2:47 PM, Carl Eastlund <carl.eastlund at gmail.com>wrote:
>
>> Scott -- I don't understand exactly what you're asking, at least not
>> based on the example you wrote.  You defined x and y, but the error is
>> about z.  Is this just a typo?  Or are you expecting a value for z to come
>> from somewhere?
>>
>> Carl Eastlund
>>
>>
>> On Sun, Jan 26, 2014 at 1:01 PM, Scott Klarenbach <scott at pointyhat.ca>wrote:
>>
>>> Thanks a lot Carl...this is very enlightening.  If I could impose one
>>> last question before I go off and digest everything:
>>>
>>> What is the "correct" approach to capturing the runtime values of any
>>> references that may be bound by the enclosing environment, for splicing
>>> into the final recursively-expanded expression?
>>>
>>> ;; example 1
>>>
>>> (define y 3)
>>> (define x 3)
>>> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))
>>> (define-dsl-syntax (bad-pred? x) (or (= 2 x) (> x z)))
>>>
>>> (recursive-expand (pred? 1))
>>> '(or (= 2 1) (> 1 3))
>>>
>>> (recursive-expand (bad-pred? 1))
>>> >> unbound identifier: z
>>>
>>>  My naive approach was to collect all the identifiers in the expression
>>> body that had bindings, compare them to the argument list of the macro with
>>> bound-identifier=? to see which ones were explicitly introduced by the
>>> user, and then eval the remaining ones at runtime in a second step in order
>>> to splice them in.
>>>
>>> I haven't tried this, and am sure people are cringing just by reading
>>> it, lol.  I know there are tons of features like marking and syntax
>>> properties and origins, etc which I don't yet understand, and which may
>>> provide a more durable solution.
>>>
>>> If needbe, I could explicitly provide to the macro the bindings I wish
>>> to capture, like postgresql does with query params...ie,
>>>
>>> (define-dsl-syntax (pred? x) (or (= 2 x) (> x $1)) #:capture (y)) ;; or
>>> something
>>>
>>> but for obvious reasons it is much better if these expressions just
>>> expanded and automatically captured any referenced values in the same way
>>> as would happen at runtime.
>>>
>>> Thanks a lot.
>>>
>>> Scott.
>>>
>>>
>>>
>>>
>>> On Sat, Jan 25, 2014 at 6:00 PM, Carl Eastlund <carl.eastlund at gmail.com>wrote:
>>>
>>>> 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
>>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> 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
>>>
>>
>>
>
>
> --
> 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/20140126/62d55a5f/attachment-0001.html>

Posted on the users mailing list.