Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing Macros in Scheme

What's the simplest way to define a capturing macro using define-syntax or define-syntax-rule in Racket?

As a concrete example, here's the trivial aif in a CL-style macro system.

(defmacro aif (test if-true &optional if-false)
    `(let ((it ,test))
        (if it ,if-true ,if-false)))

The idea is that it will be bound to the result of test in the if-true and if-false clauses. The naive transliteration (minus optional alternative) is

(define-syntax-rule (aif test if-true if-false)
    (let ((it test))
       (if it if-true if-false)))

which evaluates without complaint, but errors if you try to use it in the clauses:

> (aif "Something" (displayln it) (displayln "Nope")))
reference to undefined identifier: it

The anaphora egg implements aif as

(define-syntax aif
  (ir-macro-transformer
   (lambda (form inject compare?)
     (let ((it (inject 'it)))
       (let ((test (cadr form))
         (consequent (caddr form))
         (alternative (cdddr form)))
     (if (null? alternative)
         `(let ((,it ,test))
        (if ,it ,consequent))
         `(let ((,it ,test))
        (if ,it ,consequent ,(car alternative)))))))))

but Racket doesn't seem to have ir-macro-transformer defined or documented.

like image 228
Inaimathi Avatar asked Nov 18 '13 17:11

Inaimathi


1 Answers

Racket macros are designed to avoid capture by default. When you use define-syntax-rule it will respect lexical scope.

When you want to "break hygiene" intentionally, traditionally in Scheme you have to use syntax-case and (carefully) use datum->syntax.

But in Racket the easiest and safest way to do "anaphoric" macros is with a syntax parameter and the simple define-syntax-rule.

For example:

(require racket/stxparam)

(define-syntax-parameter it
  (lambda (stx)
    (raise-syntax-error (syntax-e stx) "can only be used inside aif")))

(define-syntax-rule (aif condition true-expr false-expr)
  (let ([tmp condition])
    (if tmp
        (syntax-parameterize ([it (make-rename-transformer #'tmp)])
          true-expr)
        false-expr)))

I wrote about syntax parameters here and also you should read Eli Barzilay's Dirty Looking Hygiene blog post and Keeping it Clean with Syntax Parameters paper (PDF).

like image 88
Greg Hendershott Avatar answered Sep 25 '22 01:09

Greg Hendershott