<div dir="ltr"><div>Indeed you are correct.  I thought about exactly *why* it's bad to use datum->syntax, and it can lead to the kind of behavior I have in the below example:</div><div><br></div><div>#lang racket</div>
<div>(define X 100)</div><div>(define y 0)</div><div>(define-syntax (set-y stx)<br>  (syntax-case stx ()<br>    [(_ n) #`(set! #,(datum->syntax #'X 'y) n)]))</div><div>(let ([y #f])<br>  (set-y 50)<br>  y)</div>
<div>y</div><div><br></div><div>Will return #f and 50 instead of 50 and 50.  The set-y call inside the let accidentally used #'X's context (the top level) and so set the y defined there as (define y 0).  Had it been written as #,(datum->syntax stx 'y), using the local context where it was called ("stx", from inside of the let where y is shadowed), it would have set the inner y and the result would be 50 and 0.  Which one is truly correct is up to the macro writer of course, but since hygienic macros are intended to preserve scope the way lambdas do, it's probably better to not use datum->syntax where "normal" behavior is desired.  So Robby's solution is more correct in this sense.  And as the creators have blogged elsewhere, even when hygiene is seemingly being broken (anaphoric if), it can still be handled without datum->syntax through syntax parameters.</div>
<div><br></div><div>Obviously they know everything I just said and more, so I say this as an addendum to my solution to anyone who might be reading it and get the wrong idea!  datum->syntax is inherently unsafe!  Does anybody have a simple example where it's especially helpful/required?</div>
</div><div class="gmail_extra"><br><br><div class="gmail_quote">On Fri, May 2, 2014 at 9:37 AM, Robby Findler <span dir="ltr"><<a href="mailto:robby@eecs.northwestern.edu" target="_blank">robby@eecs.northwestern.edu</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I don't think you want to use syntax->datum or datum->syntax in this<br>
macro. Here's another way to write it.<br>
<br>
Robby<br>
<br>
#lang racket<br>
<div>(define-syntax-rule<br>
  (with-vars (vars ...) x)<br>
  (for*/and ([vars (in-list '(#t #f))]<br>
             ...)<br>
    x))<br>
<br>
</div>(define-syntax-rule (then x y) (implies x y))<br>
(define equiv equal?)<br>
<br>
(define-for-syntax (collect-fvars stx exp)<br>
  (let loop ([exp exp])<br>
    (define (collect stx-lst)<br>
      (apply append (map loop (syntax->list stx-lst))))<br>
    (syntax-case exp (or then and not)<br>
      [(or x ...) (collect #'(x ...))]<br>
      [(and x ...) (collect #'(x ...))]<br>
      [(then x y) (collect #'(x y))]<br>
      [(equiv x y) (collect #'(x y))]<br>
      [(not x) (collect #'(x))]<br>
      [x<br>
       (identifier? #'x)<br>
       (list #'x)]<br>
      [_ (raise-syntax-error #f "unknown expression" stx exp)])))<br>
<div><br>
(define-syntax (tauta stx)<br>
  (syntax-case stx ()<br>
</div>    [(_ exp)<br>
     #`(with-vars #,(collect-fvars stx #'exp) exp)]))<br>
<div class="im HOEnZb"><br>
(tauta (or (then P Q) (then Q (not P))))<br>
</div><div class="im HOEnZb">(tauta (or P (not P))) ; excluded middle<br>
</div><div class="im HOEnZb">(tauta (or (and P Q) (or (not P) (not Q))))<br>
</div><div class="im HOEnZb">(tauta (equiv (then P Q) (then (not Q) (not P)))) ;equiv<br>
</div><div class="im HOEnZb">(tauta (then (and (then (not A) B) (then (not A) (not B))) A))<br>
;reductio ad absurdum<br>
</div><div class="im HOEnZb">(tauta (equiv (not (and A B)) (or (not A) (not B)))) ; De Morgan's law<br>
<br>
</div><div class="HOEnZb"><div class="h5">On Fri, May 2, 2014 at 8:26 AM, Sean Kanaley <<a href="mailto:skanaley@gmail.com">skanaley@gmail.com</a>> wrote:<br>
> Here's a solution (at the bottom) with the datum/syntax converters.  flatten<br>
> is built-in, free-vars is basically filtering stuff and then removing<br>
> duplicates, with-vars can take advantage of the built-in nested for, and<br>
> then tauta.<br>
><br>
> tauta can get the "quoted" form of the expression like when you used eval by<br>
> calling syntax->datum on the syntax given to tauta, minus "tauta" itself,<br>
> which is the x pattern.  But the pattern itself is not a syntax object so<br>
> requires the syntax quote "#'", at which point you can de-syntaxify it with<br>
> syntax->datum, getting '(or (and P Q) etc.).  You can try this in the REPL,<br>
> just do (syntax->datum #'(or (and P Q) etc.)).<br>
><br>
> Once free vars has this as '(P Q), map this list by re-syntaxifying the<br>
> symbols with datum->syntax.  The first parameter to datum->syntax basically<br>
> is the key to "breaking" hygiene lisp-style.  We want to say that the free<br>
> variable symbols in "x" are the *same* as in our new datum->syntaxified<br>
> symbols that get passed to with-vars to bind in the for/and, so we say they<br>
> ultimately belong to the same lexical context (your top level code) by<br>
> passing the same syntax ("stx") that tauta was called in.  In other words,<br>
> those generated vars are created as if you yourself defined them in the<br>
> module.  Without passing stx to datum->syntax, it would be as if someone<br>
> else defined them elsewhere but coincidentally with the same names, and for<br>
> safety hygienic systems disallow this shadowing (their x and your x are more<br>
> like x1 and x2, being secretly renamed by the system).  So we tell it we<br>
> really mean our own code's context.  In general, you can break hygiene this<br>
> way by using whichever syntax object refers to wherever you want to pretend<br>
> the syntax came from.<br>
><br>
> #lang racket<br>
> (require (for-syntax racket))<br>
><br>
> (define-syntax-rule (with-vars (vars ...) x)<br>
>   (for*/and ([vars (in-list '(#t #f))]<br>
>              ...)<br>
>       x))<br>
><br>
> (define-for-syntax (free-vars xs)<br>
>   (define (keyword? x)<br>
>     (member x '(and or then -> <-> not<br>
>                     equiv display displayln)))<br>
>   (remove-duplicates<br>
>    (filter (compose not keyword?)<br>
>            (flatten xs))))<br>
><br>
> (define (then x y) (or y (not x)))<br>
> (define (equiv x y) (and (then x y) (then y x)))<br>
><br>
> (define-syntax (tauta stx)<br>
>   (syntax-case stx ()<br>
>     [(_ x)<br>
>      (with-syntax ([(vars ...) (map (λ (var) (datum->syntax stx var))<br>
>                                     (free-vars (syntax->datum #'x)))])<br>
>        #'(with-vars (vars ...) x))]))<br>
><br>
</div></div><div class="HOEnZb"><div class="h5">> ____________________<br>
>   Racket Users list:<br>
>   <a href="http://lists.racket-lang.org/users" target="_blank">http://lists.racket-lang.org/users</a><br>
><br>
</div></div></blockquote></div><br></div>