do ブロックの真似事

Node.js の勉強をしているときに気晴らしで書いたマクロ。ruby の do ブロック(元ネタがあるんだっけ?)をエミュレートする感じ。

(defmacro on (head &rest rest)
  `(|on helper| ,head ,rest ()))

(defmacro |on helper| (head rest args)
  (if (null rest)
      `(,head ,@args)
      (case (car rest)
	(do  `(,head (lambda ,@(cdr rest)) ,@args))
	(do* `(,head (lambda (*) ,@(cdr rest)) ,@args))
	(t `(|on helper| ,head ,(cdr rest) (,@args ,(car rest)))))))

on のあとに式を書く。途中で do が出てくるとそこから先をラムダ式にして、関数の第一引数に突っ込む。do* は1引数関数を簡単に作るためのもので、引数名がアスタリスクになる。Common Lisp は関数と変数の名前空間が別れているので掛け算もちゃんとできる。

CL-USER> (on mapcar '(10 20 30) '(1 2 3) do (x y)
	     (+ x y))
(11 22 33)
CL-USER> (on mapcar '(1 2 3) do*
	     (* * *))
(1 4 9)