<div dir="ltr">Oh, and note that I changed define-dsl-syntax to _not_ use define-syntax-rule. That's important, if you're going to use the value of variables used in macro expansion. The define-syntax-rule form protects its output; you can't disassemble it and then run parts of the result. You can print them, quote them as s-expressions, and do other things that are sort of "superficial" from a macro standpoint, but you can't do anything that runs them or inspects their bindings. If you use regular define-syntax and never call the "syntax-protect" function, you won't run into this problem.<br>
</div><div class="gmail_extra"><br clear="all"><div>Carl Eastlund</div>
<br><br><div class="gmail_quote">On Sun, Jan 26, 2014 at 6:34 PM, Carl Eastlund <span dir="ltr"><<a href="mailto:carl.eastlund@gmail.com" target="_blank">carl.eastlund@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr"><div>You can do it like this:<br><br> <a href="https://gist.github.com/carl-eastlund/8640925" target="_blank">https://gist.github.com/carl-eastlund/8640925</a><br><br></div>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.<span class="HOEnZb"><font color="#888888"><br>
</font></span></div><div class="gmail_extra"><span class="HOEnZb"><font color="#888888"><br clear="all"><div>Carl Eastlund</div></font></span><div><div class="h5">
<br><br><div class="gmail_quote">On Sun, Jan 26, 2014 at 6:02 PM, Scott Klarenbach <span dir="ltr"><<a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">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.<div>
<br></div><div><div style="font-family:arial,sans-serif;font-size:12.727272033691406px">;; example 1 (as per our earlier discussion)</div><div style="font-family:arial,sans-serif;font-size:12.727272033691406px">(define-dsl-syntax (pred? x) (or (= 2 x) (> x 3)))</div>
<div>
<div style="font-family:arial,sans-serif;font-size:12.727272033691406px">(recursive-expand (pred? 1)) </div><div style="font-family:arial,sans-serif;font-size:12.727272033691406px">'(or (= 2 1) (> 1 3))</div><div style="font-family:arial,sans-serif;font-size:12.727272033691406px">
<br></div></div><div style="font-family:arial,sans-serif;font-size:12.727272033691406px"><div>;; example 2 (capturing a reference bound by the enclosing environment)</div><div><div>(define y 3)</div><div>(define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))</div>
</div><div><div>(recursive-expand (pred? 1)) </div><div>'(or (= 2 1) (> 1 3))</div><div><br></div></div><div><div>;; example 3 (ensuring some degree of hygiene, note the two bindings for x)</div><div>
<div>(define x 3)</div><div>(define y 3)</div>
<div>(define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))</div></div><div><div>(recursive-expand (pred? 1))</div><div>'(or (= 2 1) (> 1 3))</div></div></div><div><br></div><div>;; example 4, (I'll throw in this case, or replace the quoted output with ??? for each unbound id</div>
<div><div>(define-dsl-syntax (pred? x) (or (= 2 x) (> x z)))</div><div>(recursive-expand (pred? 1))</div><div>>> unbound identifier: z at pred? ;; or maybe</div></div><div>'(or (= 2 1) (> 1 ???))<br></div>
<div><br></div><div>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.</div>
<div><br></div><div>Thanks,</div><div>Scott.</div></div></div></div><div><div><div class="gmail_extra"><br><br><div class="gmail_quote">On Sun, Jan 26, 2014 at 2:47 PM, Carl Eastlund <span dir="ltr"><<a href="mailto:carl.eastlund@gmail.com" target="_blank">carl.eastlund@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">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?<span><font color="#888888"><br>
</font></span></div><div class="gmail_extra"><span><font color="#888888"><br clear="all"><div>Carl Eastlund</div></font></span><div><div>
<br><br><div class="gmail_quote">On Sun, Jan 26, 2014 at 1:01 PM, Scott Klarenbach <span dir="ltr"><<a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">Thanks a lot Carl...this is very enlightening. If I could impose one last question before I go off and digest everything:<div><br></div><div>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?</div>
<div><br></div><div>;; example 1</div><div><br></div><div>(define y 3)</div><div>(define x 3)</div><div>(define-dsl-syntax (pred? x) (or (= 2 x) (> x y)))</div><div>(define-dsl-syntax (bad-pred? x) (or (= 2 x) (> x z)))<br>
</div><div><br></div><div>(recursive-expand (pred? 1)) </div><div>'(or (= 2 1) (> 1 3))</div><div><br></div><div><div>(recursive-expand (bad-pred? 1)) </div><div>>> unbound identifier: z</div></div><div><br>
</div>
<div>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. </div>
<div><br></div><div>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.</div>
<div><br></div><div>If needbe, I could explicitly provide to the macro the bindings I wish to capture, like postgresql does with query params...ie, </div><div><br></div><div>(define-dsl-syntax (pred? x) (or (= 2 x) (> x $1)) #:capture (y)) ;; or something</div>
<div><br></div><div>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.</div><div><br></div><div>Thanks a lot.</div>
<span><font color="#888888">
<div><br></div><div>Scott.</div><div><br></div><div><br></div></font></span></div><div><div><div class="gmail_extra"><br><br><div class="gmail_quote">On Sat, Jan 25, 2014 at 6:00 PM, Carl Eastlund <span dir="ltr"><<a href="mailto:carl.eastlund@gmail.com" target="_blank">carl.eastlund@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div><div><div><div><div><div>Scott,<br><br></div>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.<br>
<br></div>Where local-expand is going to bite you is when the definition of pred? uses a macro at its top level. For instance:<br><br></div> (define-syntax-rule (pred? x) (or (< x 3) (> x 7)))<br><br></div>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?.<br>
<br></div>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.<br>
<br></div>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: <a href="https://gist.github.com/carl-eastlund/8626893" target="_blank">https://gist.github.com/carl-eastlund/8626893</a><span><font color="#888888"><br>
<br clear="all"><div>Carl Eastlund</div></font></span><div><div>
<br><div class="gmail_quote">On Fri, Jan 24, 2014 at 1:30 PM, Scott Klarenbach <span dir="ltr"><<a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr"><div>Just an update, I was able to make this work.</div><div><br></div><div>#lang racket</div><div>(require (for-syntax racket/syntax syntax/stx))</div><div><br></div><div>(define-syntax-rule (pred? x) (> 3 x))</div>
<div>
<div><br></div><div>(define-for-syntax (recursive-expand stx)</div><div> (let loop ([l (syntax->list stx)])</div><div><span style="white-space:pre-wrap"> </span>(cond [(stx-null? l) l]</div><div><span style="white-space:pre-wrap"> </span> [(stx-pair? (stx-car l))</div>
<div><span style="white-space:pre-wrap"> </span> (cons (loop (stx-car l)) (loop (stx-cdr l)))]</div></div><div><span style="white-space:pre-wrap"> </span> [(equal? 'pred? (syntax->datum (stx-car l)))</div><div>
<span style="white-space:pre-wrap"> </span> (local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression #f)] ;; this works</div><div><div><span style="white-space:pre-wrap"> </span> [else</div><div>
<span style="white-space:pre-wrap"> </span> (cons (stx-car l) (loop (stx-cdr l)))])))</div>
<div><br></div><div>(define-syntax (test stx)</div><div> (syntax-case stx ()</div><div><span style="white-space:pre-wrap"> </span>[(_ x)</div></div><div><span style="white-space:pre-wrap"> </span> (with-syntax ([expanded (recursive-expand #'x)])</div>
<div><span style="white-space:pre-wrap"> </span> #''expanded)]))</div><div><br></div><div>(module+ test</div><div> (require rackunit)</div><div> (check-equal? (test (or (< 10 x) (pred? y)))</div><div><span style="white-space:pre-wrap"> </span>'(or (< 10 x) (> 3 y))))</div>
<div><br></div><div>The code I couldn't figure out last night was:</div><div>(local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression #f)]<br></div><div><div><br></div><div>Thanks.</div><div><br>
</div>-- <br>Talk to you soon,<br>
<br>Scott Klarenbach<br><br>PointyHat Software Corp.<br><a href="http://www.pointyhat.ca" target="_blank">www.pointyhat.ca</a><br>p <a href="tel:604-568-4280" value="+16045684280" target="_blank">604-568-4280</a><br>e <a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a><br>
<span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">200-1575 W. Georgia</span><br>Vancouver, BC <span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">V6G2V3</span><br>
<br>_______________________________________<br>To iterate is human; to recur, divine
</div></div>
</blockquote></div><br></div></div></div></div>
</blockquote></div><br><br clear="all"><div><br></div>-- <br>Talk to you soon,<br><br>Scott Klarenbach<br><br>PointyHat Software Corp.<br><a href="http://www.pointyhat.ca" target="_blank">www.pointyhat.ca</a><br>p <a href="tel:604-568-4280" value="+16045684280" target="_blank">604-568-4280</a><br>
e <a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a><br><span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">200-1575 W. Georgia</span><br>
Vancouver, BC <span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">V6G2V3</span><br><br>_______________________________________<br>To iterate is human; to recur, divine
</div>
</div></div></blockquote></div><br></div></div></div>
</blockquote></div><br><br clear="all"><div><br></div>-- <br>Talk to you soon,<br><br>Scott Klarenbach<br><br>PointyHat Software Corp.<br><a href="http://www.pointyhat.ca" target="_blank">www.pointyhat.ca</a><br>p <a href="tel:604-568-4280" value="+16045684280" target="_blank">604-568-4280</a><br>
e <a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a><br><span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">200-1575 W. Georgia</span><br>
Vancouver, BC <span style="color:rgb(34,34,34);font-size:13px;font-family:arial,sans-serif">V6G2V3</span><br><br>_______________________________________<br>To iterate is human; to recur, divine
</div>
</div></div></blockquote></div><br></div></div></div>
</blockquote></div><br></div>