表題

基本的な syntax-rules の拡張

著者

Taylor Campbell

著者

この SRFI は現在「確定」の状態である。 SRFI の各状態の説明については ここ を参照せよ。 この SRFI に関する議論については メーリングリストのアーカイブ を参照せよ。

概要

この SRFI では R5RS1syntax-rules パターン言語に対して2つの拡張を提案する。 1つめの拡張は syntax-rules マクロがマクロを生成することを許すことである。 ここで、マクロから生成されたマクロは、 マクロを生成しているマクロで使用されていない省略記号を使用することができる。 2つめの拡張は「末尾パターン」を許すことである。

論拠

マクロを生成するマクロは、かなり一般的に使われており、 様々な状況において非常に便利である。 たとえば、ローカル継続マクロのための CPS マクロ2 がそうである。 現状の R5RS では、syntax-rules のテンプレートにおいて、 リテラルの省略記号を生成することができない。 テンプレート内のすべての省略記号は、そのマクロにより処理されてしまう。 そのため、マクロを生成するマクロには制限が課されることになり、 生成されたマクロは省略記号を利用することができない。 これは厳しい制限であるが、syntax-rules 構文に簡単な拡張を導入することで、この制限を取り除くことができる。

それに加えて、 省略記号の前に位置する有限長のシーケンスにマッチすることができるだけでなく、 省略記号の後に位置するシーケンスにマッチすることができれば、便利である。 このような「末尾パターン」(tail pattern) は R5RS の syntax-rules ではサポートされていない。 そこで、この SRFI では syntax-rules に末尾パターンを追加することを提案する。

仕様

syntax-rules 構文を拡張し、 リテラル識別子リストの前にトークンを1つ記述できるようにする。

  (syntax-rules [<ellipsis-identifier>] (<literal-identifier> ...)
    (<pattern> <template>)
    ...)

ellipsis-identifier には、省略記号として使うトークンを指定する。 デフォルトでは通常の R5RS1 の省略識別子 (...) であるが、::: のような任意の識別子を指定することができる。 この識別子の束縛スコープは、そのトランスフォーマのルール内であるとする。 R5RS 4.3 節で規定されているとおり、 マクロ処理系はハイジニックな名前変更を行って、 束縛のレキシカル スコープを保護しなければならない。

また、syntax-rules パターン言語を拡張して「末尾パターン」を導入する。 <pattern> に以下の節を追加する。

 (<pattern> ... <ellipsis> <pattern> ...)
#(<pattern> ... <ellipsis> <pattern> ...)

そして、syntax-rules のパターン マッチングの意味論に、 以下の節を追加する。

使用例

;;; Examples of the user-specified ellipsis token extension

;;; Utility macro for CPS macros
(define-syntax apply-syntactic-continuation
  (syntax-rules ()
    ((apply-syntactic-continuation (?k ?env ...) . ?args)
     (?k ?env ... . ?args))))

;;; Generates a list of temporaries, for example to implement LETREC
;;; (see below), and 'returns' it by CPS.
(define-syntax generate-temporaries
  (syntax-rules ()
    ((generate-temporaries ?origs ?k)
     (letrec-syntax
         ((aux (syntax-rules ::: ()
                 ;; We use a trick here: pass the continuation again
                 ;; to AUX in case it contains ellipsis.  If we stuck
                 ;; it right into AUX's template, AUX would process the
                 ;; ellipsis in ?K as ellipsis for something in the AUX
                 ;; macro.
                 ((aux ?temps () ?k*)
                  (apply-syntactic-continuation ?k* ?temps))
                 ;; Be careful about the ellipsis!
                 ((aux (?temp :::) (?x ?more :::) ?k*)
                  (aux (?temp ::: new-temp)
                       (?more :::)
                       ?k*)))))
       (aux () ?origs ?k)))))

;;; Instead of having lots of auxiliary clauses in LETREC, like in the
;;; R5RS sample implementation, we use GENERATE-TEMPORARIES.  Instead
;;; of 'returning,' like an ordinary function, we create a continuation
;;;  for GENERATE-TEMPORARIES with LET-SYNTAX.  Since this continuation
;;; uses ellipsis, we must use the ellipsis token extension.
(define-syntax letrec
  (syntax-rules ()
    ((letrec ((?var ?init) ...) ?body1 ?body2 ...)
     (let-syntax
         ((k (syntax-rules ::: ()
               ;; Use the same trick as with the continuations in
               ;; GENERATE-TEMPORARIES.  Be careful about the ellipsis!
               ((k ((?var* ?init*) :::)
                   (?body1* ?body2* :::)
                   ;; Here are the actual arguments to the continuation
                   ;; -- the previous bits of the pattern were just the
                   ;; 'environment' of the continuation --:
                   (?temp :::))
                (let ((?var* (if #f #f)) ; Get an 'unspecific' value.
                      :::)
                  (let ((?temp ?init*) :::)
                    (set! ?var* ?temp) :::
                    (let () ?body1* ?body2* :::)))))))
       (generate-temporaries (?var ...)
         ;; Pass K the environment.  GENERATE-TEMPORARIES will add the
         ;; temporary variable list argument.
         (k ((?var ?init) ...) (?body1 ?body2 ...)))))))

;;; The next example uses two other macros that we don't define here:
;;; SYNTACTIC-SYMBOL? and UNION.  (SYNTACTIC-SYMBOL? <x> <sk> <fk>)
;;; expands to SK if X is a symbol or FK otherwise.  (UNION <s1> <s2>
;;; <k>) applies K with APPLY-SYNTACTIC-CONTINUATION to the union of
;;; the syntactic lists S1 and S2.  Both of SYNTACTIC-SYMBOL? and UNION
;;; are possible to implement here, but we sha'n't bother with them, as
;;; we wish only to demonstrate an example of macros generating macro-
;;; generating macros, and they provide no such examples.

;;; ALL-SYMBOLS digs out all the symbols in a syntax.
(define-syntax all-symbols
  (syntax-rules ()
    ((all-symbols (?x . ?y) ?k)
     (let-syntax
         ((k (syntax-rules :::0 ()
               ((k ?y* ?k*  (?symbol :::0))
                (let-syntax
                    ((k* (syntax-rules :::1 ()
                           ;; Doubly nested ellipsis: we use another
                           ;; distinct ellipsis token.
                           ((k* ?k** (?symbol* :::1))
                            (union (?symbol  :::0)
                                   (?symbol* :::1)
                                   ?k**)))))
                  (all-symbols ?y* (k* ?k*)))))))
       (all-symbols ?x (k ?y ?k))))

    ((all-symbols #(?x ...) ?k)
     (all-symbols (?x ...) ?k))

    ((all-symbols ?x ?k)
     (syntax-symbol? ?x
       (apply-syntactic-continuation ?k (?x))
       (apply-syntactic-continuation ?k ())))))

(all-symbols (foo 4 bar #(#t (baz (#f quux)) zot) (mumble #(frotz)))
             (quote)) ; => (frotz mumble zot quux baz bar foo)

;;; This example demonstrates the hygienic renaming of the ellipsis
;;; identifiers.

(let-syntax
    ((f (syntax-rules ()
          ((f ?e)
           (let-syntax
               ((g (syntax-rules ::: ()
                     ((g (??x ?e) (??y :::))
                      '((??x) ?e (??y) :::)))))
             (g (1 2) (3 4)))))))
  (f :::))
    ; => ((1) 2 (3) (4)), if hygienic rules of ellipsis identifiers are
    ;      correctly implemented, not ((1) (2) (3) (4))
;;; --------------------
;;; Examples of tail patterns

;;; This example of the tail pattern extension is a crippled version of
;;; R5RS's BEGIN special form.  (It is crippled because it does not
;;; support internal definitions or commands within its body returning
;;; fewer or more than one value.)

(define-syntax fake-begin
  (syntax-rules ()
    ((fake-begin ?body ... ?tail)
     (let* ((ignored ?body) ...) ?tail))))

;;; For example,
;;;   (FAKE-BEGIN
;;;     (DISPLAY "Hello,")
;;;     (WRITE-CHAR #\SPACE)
;;;     (DISPLAY "world!")
;;;     (NEWLINE))
;;; would expand to
;;;   (LET* ((IGNORED (DISPLAY "Hello,"))
;;;          (IGNORED (WRITE-CHAR #\SPACE))
;;;          (IGNORED (DISPLAY "world!")))
;;;     (NEWLINE))

(let-syntax
    ((foo (syntax-rules ()
            ((foo ?x ?y ... ?z)
             (list ?x (list ?y ...) ?z)))))
  (foo 1 2 3 4 5))
    ; => (1 (2 3 4) 5)

実装

ここで提案した拡張を実装した、 Alexpander と EIOD という2つのマクロ エクスパンダを実装例として挙げる。 Alexpander は syntax-rules マクロ処理系のための 完全で洗練されたエクスパンダである。 EIOD は R5RS の eval の実装であり、 内部的なマクロ エクスパンダを必要とする。 両方とも Al* Petrofsky が実装したものである。 著作権とライセンスについては、それぞれのソースコードを参照せよ。 Alexpander は <http://srfi.schemers.org/srfi-46/alexpander.scm> から入手できる。 EIOD は <http://srfi.schemers.org/srfi-46/eiod.scm> から入手できる。

謝辞

Al* Petrofsky はメーリングリストにおいて、 この SRFI の基本設計に関する数多くの重要な提案を行い、 それが最終的な成果に強い影響を与えた。 厚く感謝する。

参考文献

  1. Richard Kelsey, William Clinger, and Jonathon Rees (editors).
    The Revised5 Report on the Algorithmic Language Scheme
    Higher-Order and Symbolic Computation, Vol. 11, No. 1, September, 1998, and ACM SIGPLAN Notices, Vol. 33, No. 9, October, 1998.
    http://www.schemers.org/Documents/Standards/R5RS/
  2. Erik Hilsdale and Daniel P. Friedman.
    Writing Macros in Continuation-Passing Style
    Scheme and Functional Programming 2000, September, 2000.
    http://www.ccs.neu.edu/home/matthias/Scheme2000/hilsdale.ps

著作権

Copyright (C) Taylor Campbell (2005). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


編集者: David Rush
最終更新日時: Sun Jan 28 13:40:19 MET 2007