マクロを定義するマクロ


Tags: R6RS, マクロ

(rnrs enums) の define-enumeration マクロの

(define-enumeration color
  (black white purple maroon)
  color-set)

という記述は、おおよそ

(define-syntax color
  (lambda (x)
    (syntax-case x ()
      ((_ sym)
       (memq (syntax->datum #'sym) '(black white purple maroon))
       #''sym))))

(define-syntax color-set
  (lambda (x)
    (syntax-case x ()
      ((_ sym ...)
       (for-all (lambda (s) (memq s '(black white purple maroon)))
                (syntax->datum #'(sym ...))))
      #'((enum-set-constructor (make-enumeration '(black white purple maroon)))
         '(sym ...)))))

というふたつのマクロに展開される。 syntax-case ではマクロ定義中の識別子の意味は通常の識別子と同じく字句構文上もっとも近い識別子と同一と解釈されるため、伝統的なマクロのように quasiquote の入れ子に悩まされることはない。その代わりに展開形に ... を含めたい場合に、現在のマクロ定義のものと混同しないために (... ...) と書く必要がある。

それを踏まえた上で define-enumeration マクロは次のように定義できる。

(import (rnrs base)
        (rnrs syntax-case)
        (except (rnrs enums) define-enumeration))

(define-syntax define-enumeration
  (syntax-rules ()
    ((_ type-name (sym ...) constructor-syntax)
     (begin
       (define-syntax type-name
         (lambda (x)
           (syntax-case x ()
             ((_ sym1)
              (memq (syntax->datum #'sym1) '(sym ...))
              #''sym1))))
       (define-syntax constructor-syntax
         (lambda (x)
           (syntax-case x ()
             ((_ sym2 (... ...))
              (for-all (lambda (s) (memq s '(sym ...)))
                       (syntax->datum #'(sym2 (... ...))))
              #'((enum-set-constructor (make-enumeration '(sym ...)))
                 '(sym2 (... ...)))))))))))