<div dir="ltr">Carl,<div><br></div><div>Don't panic :).</div><div><br></div><div>I'm not trying to reuse the expanded syntax at runtime. I'm just trying to parse it out for my own purposes in the context of a dsl that I fully control. I'm happy to strip all context and just have a list of symbols if need-be. My problem is much simpler, in that I wish only to know how to recursively expand the symbols in an elegant form, as I'll show in an example at the end.</div>
<div><br></div><div>First - some context:</div><div><br></div><div>I'm trying to create/experiment with a poor-mans LINQ provider in Racket.</div><div><br></div><div>I have a macro (define/expr ...). What this does is bind the syntax provided at runtime (as though it was a regular define), but in addition it stores the syntax in an expression tree bound at compile time for *possible* future processing. This means that</div>
<div><br></div><div>(λ (x) (= x 3))<br></div><div><br></div><div>can be used as a filter predicate on a list?, but also as a where-clause predicate in a sql query, with exactly the same syntax. All that changes is the data source.</div>
<div><br></div><div>Anything defined with (define/expr) can be used at runtime in racket as though it was defined normally, and I make no attempt to mess with those semantics. However, the same expression might also be used on a datasource that is intended to target a twitter feed, or a redis store. In this case, I want to expand any internal expression clauses that were defined using "define/expr", leaving an expression containing only racket "primitives" - meaning anything not explicitly defined using my macro. I can then map over the remaining bindings and throw syntax errors ala LINQ depending on the intended source target, ie "vector-map has no supported translation to sql". (even though the lambda in question might be perfectly valid if used in a different context).</div>
<div><br></div><div>If the user chose to redefine "or" in that context, fine. The racket runtime will deal with that if it's used there. If; however, the expression is used in the context of a generic "sql-provider", well, "or" can only mean one thing.<br>
</div><div><br></div><div>This provides parameterization and composiblity - features sorely lacking from say, SQL.</div><div><br></div><div>That being said, I'm sure my mistake is rather junior, I just can't seem to figure it out. The following code recursively replaces any occurrence of an expression (kbach? ...) with (weentucky? ...). This is just my training wheel code to ensure that I have the recursion part correct.</div>
<div><br></div><div>(require (for-syntax racket/syntax syntax/stx))</div><div><br></div><div><div>(define-for-syntax (recursive-expand stx)</div><div> (let loop ([l (syntax->list stx)])</div><div><span class="" style="white-space:pre"> </span>(cond [(stx-null? l) l]</div>
<div><span class="" style="white-space:pre"> </span> [(stx-pair? (stx-car l))</div><div><span class="" style="white-space:pre"> </span> (cons (loop (stx-car l)) (loop (stx-cdr l)))]</div><div><span class="" style="white-space:pre"> </span> [(equal? 'kbach? (syntax->datum (stx-car l))) ;; line a</div>
<div><span class="" style="white-space:pre"> </span> (cons 'weentucky? (loop (stx-cdr l)))] ;; line b</div><div><span class="" style="white-space:pre"> </span> [else</div><div><span class="" style="white-space:pre"> </span> (cons (stx-car l) (loop (stx-cdr l)))])))</div>
</div><div><br></div><div><div>(define-syntax (test stx)</div><div> (syntax-case stx ()</div><div><span class="" style="white-space:pre"> </span>[(_ x)</div><div><span class="" style="white-space:pre"> </span> (let ([expanded (datum->syntax #f (recursive-expand #'x))])</div>
<div><span class="" style="white-space:pre"> </span> (printf "expanded: ~s\n" (syntax->datum expanded))</div><div><span class="" style="white-space:pre"> </span> #''done)]))</div></div><div><br></div>
<div>> (test (kbach? (kbach? 1)))</div><div>expanded: (weentucky? (weentucky? 1))</div><div><br></div><div>> (test ((kbach? (kbach? (kbach? 1))) (other 1)))</div><div>expanded: ((weentucky? (weentucky? (weentucky? 1))) (other 1))<br>
</div><div><br></div><div>This works as I want, but I can't take the next step. line a and line b above, is where I wish to local-expand the expression. But it's tricky. Something like:</div><div><br></div><div>
<div>[(equal? 'kbach? (syntax->datum (stx-car l))) ;; line a</div><div> (cons (local-expand l) (local-expand (loop (stx-cdr l)))) ;; line b</div></div><div><br></div><div>What I really want here is to local expand the expression "l", in the event that (stx-car l) is a match, but only after all of the nested operands of "l" that might also have a (stx-car l) match have already been local-expand <i>ed.</i></div>
<div><i><br></i></div><div><i>Note: I'm not really checking the proc name in line a, in reality it's a struct that I bind at compile time using your earlier tutorial on structs and syntax-local-value (thanks by the way!)... I'm just simplifying here.</i></div>
<div><br></div><div>sk</div><div><br></div><div><br></div><div><br></div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Thu, Jan 23, 2014 at 7:06 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><div><div><div>Scott,<br><br></div>What you're doing isn't possible -- isn't even meaningful -- in general. You want to expand (MACRO-NAME (pred-a? z) (pred-b? z)) into (MACRO-NAME (equal? z "hey") (equal? z "there")) for some arbitrary MACRO-NAME you haven't listed. That's not at all safe! For instance, you did it with the name or. Well, what if we were in this context...<br>
<br></div>(define-syntax-rule (or e ...) (quote (e ...)))<br><br></div>Now you're not rewriting equals to equals with respect to the definitions of pred-a? and pred-b?, you're changing the value of a quoted constant by rewriting the arguments to or. Unless you know the macros whose arguments you're expanding, you don't know which positions in their arguments represent expressions. Some positions might be expressions, some might be binding names, some might be quoted data, some might be other things like match patterns that have a wholly different meaning.<br>
<br></div>There's a reason local-expand is as limited as it is. Macro expansion is very hard to do inside anything except a fully expanded context. Usually when we want to control expansion somewhere else, we either local expand all the way down, or we delay the effect somehow. For instance, you might wrap the expression with let-syntax to rebind the names pred-a? and pred-b?, shadowing them with macros that do the substitution you want when expansion naturally reaches them.<br>
</div><div class="gmail_extra"><br clear="all"><div>Carl Eastlund</div>
<br><br><div class="gmail_quote"><div><div class="h5">On Thu, Jan 23, 2014 at 8:06 PM, Scott Klarenbach <span dir="ltr"><<a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a>></span> wrote:<br>
</div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div><div class="h5">
<div dir="ltr">I'm trying use local-expand <div><br></div><div>(<a href="http://docs.racket-lang.org/reference/stxtrans.html?q=local-expand&q=local-expand#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29" target="_blank">http://docs.racket-lang.org/reference/stxtrans.html?q=local-expand&q=local-expand#%28def._%28%28quote._~23~25kernel%29._local-expand%29%29</a>)</div>
<div><br><div>to partially expand sub-expressions. The only expressions I want to expand are known in advance, I'm just having trouble coming up with an elegant recursive algorithm for doing this.<div><br></div><div>
For example:</div><div><br></div><div>(define (pred-a? x) (equal? x "hey"))</div><div>(define (pred-b? y) (equal? y "there"))</div><div>(define other-expr (lambda (z) (or (equal? z "you") (pred-a? z) (pred-b? z)))</div>
<div><br></div><div>I'd like to expand other-expr like so that:</div><div><br></div><div>(expand other-expr) >> (lambda (z) (or (equal? z "you") (equal? z "hey") (equal? z "there"))</div>
<div><br></div><div>local-expand sort of works, but provides either too little expansion (#f stop-ids) whereby only the outer macro is expanded, or else it provides too much expansion (list of stop-ids), whereby "and" and "or" etc is expanded into let-values bindings.</div>
<div><br></div><div>What I'd like is something in between. Rather than a "stop-list", I want an "include-list". I don't want or / and to expand to let-values, but I do want any of my special nested expressions (that might be 3 levels deep in an or clause) to expand inside the original or.<br>
</div><div><br></div><div>I figure this is not possible using the built-in functions, and so set out trying to build a recursive function that would reconstruct the expression and selectively expand the inner procedures I wish by applying local-expand to the nested operands.</div>
<div><br></div><div>My question is: </div><div><br></div><div>1.) Is there a simple library way to do this with local-expand that I'm missing?</div><div>2.) Does anyone have a hint or code example to do this manually using recursion? It should be simple enough but I'm embarrassed to admit how long I've wrestled with it.</div>
<div><br></div><div>Thanks.</div><div><br></div><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></div>
<br></div></div>____________________<br>
Racket Users list:<br>
<a href="http://lists.racket-lang.org/users" target="_blank">http://lists.racket-lang.org/users</a><br>
<br></blockquote></div><br></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 604-568-4280<br>
e <a href="mailto:scott@pointyhat.ca" target="_blank">scott@pointyhat.ca</a><br><span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:13px;background-color:rgb(255,255,255)">200-1575 W. Georgia</span><br>
Vancouver, BC <span style="color:rgb(34,34,34);font-family:arial,sans-serif;font-size:13px;background-color:rgb(255,255,255)">V6G2V3</span><br><br>_______________________________________<br>To iterate is human; to recur, divine
</div>