For those interested, it turns out you can get a loose approximation of
the van Tonder system in Racket in just a few dozen lines of code.
Namely, you can write a helper that undoes the macro-introduction scope
added by the Racket macro system:

    (begin-for-syntax
      (define ((make-unscoped-transformer proc) stx)
        (syntax-local-introduce (proc (syntax-local-introduce stx)))))

Then you can write some functions and forms for keeping track of which
scopes to add when users write quote-syntax:

    (begin-for-syntax
      (define current-syntax-introducer (make-parameter #f))
      (define (current-syntax-introduce stx)
        ((or (current-syntax-introducer)
             (make-syntax-introducer))
         stx))

      (define (call-with-shared-syntax-introducer proc)
        (if (current-syntax-introducer)
            (proc)
            (parameterize ([current-syntax-introducer
                            (make-syntax-introducer)])
              (proc))))
      (define (call-with-masked-syntax-introducer proc)
        (parameterize ([current-syntax-introducer #f])
          (proc)))

      (define-simple-macro (with-shared-syntax-introducer
                             body:expr ...+)
        (call-with-shared-syntax-introducer (λ () body ...)))
      (define-simple-macro (with-masked-syntax-introducer
                             body:expr ...+)
        (call-with-masked-syntax-introducer (λ () body ...))))

You can define an introducing variant of quote-syntax in terms of
Racket’s quote-syntax:

    (begin-for-syntax
      (define-simple-macro (quote-syntax form)
        (current-syntax-introduce (quote-syntax/no-introduce form))))

And finally, you can implement syntax and quasisyntax in terms of these
other forms and functions. That part is the most amount of work, so I
haven’t implemented full versions of either, but I implemented
simplified versions that don’t handle ellipses and generate less optimal
code. The only interesting thing in their implementations is the
placement of with-shared-syntax-introducer and
with-masked-syntax-introducer. Both expand into uses of
with-shared-syntax-introducer, which is wrapped around the entire
expansion, and unsyntax must wrap its expression in
with-masked-syntax-introducer in its expansion. This produces a system
that seems to have the properties of van Tonder’s system in simple
situations.

Experimentation leads to some interesting behavior. For example, the
following macro is completely uninteresting in a system that uses scoped
expansion, but it’s quite interesting in one that uses scoped quotation:

    (define x 'module)

    (define-syntax mac
      (make-unscoped-transformer
       (syntax-parser
         [(_)
          #`(let ([x 'local])
              (list x #,#'x))])))

    (mac)

Under scoped-expansion, the program produces the boring result '(local
local), but under scoped-quotation, it produces the much more
interesting result '(local module)! Maybe some people would find this
confusing, but I think it’s a little neat.

If anyone is interested in playing with my hacky, incomplete, and
probably buggy embedding of this system in Racket, I’ve posted it here:

  https://gist.github.com/lexi-lambda/a32aab1bb3eccd416764ef90cbd55b67

As a testament to the power of Racket’s macro system and its
macro-writing facilities, the whole thing is only 80 lines of code.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to