[racket] selectively local-expand sub-expressions
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
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140126/af38333b/attachment-0001.html>