[racket] Macros baffle me

From: Sean Kanaley (skanaley at gmail.com)
Date: Fri May 2 09:26:16 EDT 2014

Here's a solution (at the bottom) with the datum/syntax converters.
flatten is built-in, free-vars is basically filtering stuff and then
removing duplicates, with-vars can take advantage of the built-in nested
for, and then tauta.

tauta can get the "quoted" form of the expression like when you used eval
by calling syntax->datum on the syntax given to tauta, minus "tauta"
itself, which is the x pattern.  But the pattern itself is not a syntax
object so requires the syntax quote "#'", at which point you can
de-syntaxify it with syntax->datum, getting '(or (and P Q) etc.).  You can
try this in the REPL, just do (syntax->datum #'(or (and P Q) etc.)).

Once free vars has this as '(P Q), map this list by re-syntaxifying the
symbols with datum->syntax.  The first parameter to datum->syntax basically
is the key to "breaking" hygiene lisp-style.  We want to say that the free
variable symbols in "x" are the *same* as in our new datum->syntaxified
symbols that get passed to with-vars to bind in the for/and, so we say they
ultimately belong to the same lexical context (your top level code) by
passing the same syntax ("stx") that tauta was called in.  In other words,
those generated vars are created as if you yourself defined them in the
module.  Without passing stx to datum->syntax, it would be as if someone
else defined them elsewhere but coincidentally with the same names, and for
safety hygienic systems disallow this shadowing (their x and your x are
more like x1 and x2, being secretly renamed by the system).  So we tell it
we really mean our own code's context.  In general, you can break hygiene
this way by using whichever syntax object refers to wherever you want to
pretend the syntax came from.

#lang racket
(require (for-syntax racket))

(define-syntax-rule (with-vars (vars ...) x)
  (for*/and ([vars (in-list '(#t #f))]
             ...)
      x))

(define-for-syntax (free-vars xs)
  (define (keyword? x)
    (member x '(and or then -> <-> not
                    equiv display displayln)))
  (remove-duplicates
   (filter (compose not keyword?)
           (flatten xs))))

(define (then x y) (or y (not x)))
(define (equiv x y) (and (then x y) (then y x)))

(define-syntax (tauta stx)
  (syntax-case stx ()
    [(_ x)
     (with-syntax ([(vars ...) (map (λ (var) (datum->syntax stx var))
                                    (free-vars (syntax->datum #'x)))])
       #'(with-vars (vars ...) x))]))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20140502/305cdad4/attachment.html>

Posted on the users mailing list.