util.match は便利だけどどう展開されるの?

Gauche の util.match (Andrew Wright の match)はとても便利で自前の処理系にも移植して使っています。
自前のコンパイラ内でも match を使っていてコードの可読性と拡張性を高めてくれています。


ただ match がどう展開されるかを見たことがなかったので試してみました。
match はただのマクロなので Gauche の %macroexpand を使えば展開後の形が分かります。

(%macroexpand
 (match x
   [('CONSTANT y 'PUSH . rest)
    `(CONSTANT_PUSH ,y ,@rest)]
   [('REFER_LOCAL0 y 'PUSH . rest)
    `(CONSTANT_PUSH0 ,y ,@rest)]))

(if (pair? x)
    (if (equal? (car x) 'CONSTANT)
        (if (and (pair? (cdr x)) (pair? (cddr x)) (equal? (caddr x) 'PUSH))
            ((lambda (y rest) `(CONSTANT_PUSH ,y ,@rest)) (cadr x) (cdddr x))
            (match:error x))
        (if (and (equal? (car x) 'REFER_LOCAL0) (pair? (cdr x)) (pair? (cddr x)) (equal? (caddr x) 'PUSH))
            ((lambda (y rest) `(CONSTANT_PUSH0 ,y ,@rest)) (cadr x) (cdddr x))
            (match:error x)))
    (match:error x))

let を使わず lambda の呼び出しなのはなぜだろうか?自前の処理系の match はそのうち let を吐くようにしよう。
x が pair? を満たすかどうかは一度しかチェックされないのが良いですね。


not を使ってみた。
おお。手で比較を書いでも同じようになるのでこれは match を信用しても良さそうです。

(%macroexpand
 (match x
   [((and y (not 'CONSTANT)) NUMBERP)
    y]))

(if (pair? x)
    (if (equal? (car x) 'CONSTANT)
        (match:error x)
        (if (and (pair? (cdr x)) (null? (cddr x)))
            ((lambda (y NUMBERP) y) (car x) (cadr x))
            (match:error x)))
    (match:error x))


欲を言えばシンボルを使っているときは比較手続きを eq? にしてくれないだろうか。