LoginSignup
2
3

More than 5 years have passed since last update.

Scheme でアナフォリックマクロ

Posted at

先日、「Gauche の Explicit-renaming でスレッドマクロ」という表題で Scheme 関連の記事が投稿されていたのを読みました。 この元記事は Clojure にあるスレッドマクロを再現しようとする試みですが、引数を適用する箇所を自由にする拡張の部分が興味深いです。

このマクロはアナフォリックマクロになっています。 アナフォリックマクロというのは特定の名前の束縛を暗黙に行うようなマクロのことです。

syntax-rules でアナフォリックマクロ

アナフォリックマクロは syntax-rules で出来ない種類のマクロであると説明されることがしばしばあります。 しかし、少し回りくどい技巧を用いれば不可能ではありません。

実際に syntax-rules でやってみます。 ここでは R7RS の仕様に沿ってライブラリとして書きましたが、 define-library 構文を除けば R5RS にも R6RS にも合致するはずです。

(define-library (threading-macro)
  (export % -> ->>)
  (import (scheme base))

  (begin
    (define-syntax % (syntax-rules ()))

    (define-syntax let1
      (syntax-rules ()
        ((_ var expr b0 b1 ...)
         (let ((var expr)) b0 b1 ...))))

    (define-syntax if-let1
      (syntax-rules ()
        ((_ var expr then)
         (let1 var expr (if var then)))
        ((_ var expr then alt)
         (let1 var expr (if var then alt)))))

    (define-syntax ->
      (syntax-rules ()
        ((_ cur) cur)
        ((_ cur (proc args ...) rest ...)
         (if-find-% (args ...) (args ...)
                    (cur (if-let1 next (proc args ...) (-> next rest ...) #f))
                    (if-let1 next (proc cur args ...) (-> next rest ...) #f)))
        ((_ cur proc rest ...)
         (if-let1 next (proc cur) (-> next rest ...) #f))))

    (define-syntax ->>
      (syntax-rules ()
        ((_ cur) cur)
        ((_ cur (proc args ...) rest ...)
         (if-find-% (args ...) (args ...)
                    (cur (if-let1 next (proc args ...) (->> next rest ...) #f))
                    (if-let1 next (proc args ... cur) (->> next rest ...) #f)))
        ((_ cur proc rest ...)
         (if-let1 next (proc cur) (->> next rest ...) #f))))

    (define-syntax if-find-%
      (syntax-rules (%)
        ((_ (x . xs) (y . ys) t f)
         (if-find-% x y
                    t
                    (if-find-% xs ys t f)))
        ((_ % x t f) (let1 x . t))
        ((_ x y t f) f)))

    ))

実行例

このように使うと……

(import (scheme base)
        (scheme write)
        (threading-macro))

(write (->> 2 (list % 1)))

きちんと (2 1) という結果が表示されます。

どうなってんの?

特定の名前の束縛を暗黙に作るのがアナフォリックマクロであると最初に説明しました。 逆に言えば、それが出来ないはずの syntax-rules では束縛される名前は陽に与えなければならないということです。

いや、 % は与えられているではないか! というのが syntax-rules でアナフォリックマクロを作る考え方です。

(->> 2
     (list % ;; ← ここに % は有るじゃん!
           1))

与えられたフォームの中から % を探してそれを出力に転写することでアナフォリックマクロが実現できるのです。

欠陥

とはいうものの、感覚的に不自然な挙動をする場合もあるので、汎用的なユーティリティとしてはあまりお勧めできるものではありません。

元記事で提示されているマクロと同程度ならば syntax-rules でも出来そうだと思ったことからやってみたという余興なので、あまり積極的に参考にしないでください。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3