『実践Common Lisp』3.8のwhereを関数で実装する

マクロで実装された where を他の言語的な発想で、つまり関数(クロージャ)を使って実装したらどうなるか、考えてみた。

(defun make-comparison-fun (field value)
  (lambda (cd) (equal (getf cd field) value)))
 
(defun all (fun lst)
  (cond ((not lst) t)
	((not (funcall fun (car lst))) nil)
	(t (all fun (cdr lst)))))
 
(defun where-fun (&rest clauses)
  #'(lambda (cd)
      (all #'(lambda (comp-fun) (funcall comp-fun cd))
	   (loop while clauses
	      collecting (make-comparison-fun (pop clauses) (pop clauses))))))

こうして書き比べてみると、関数という単位でしか処理を分割できないのはとても不自由で、不自然だとすら感じてしまう(そう感じてしまう自分に驚く)。make-comparison-fun の本当の意図は「今すぐには評価しない、後で評価するためのコードブロックを作る」ことであって、関数という形式にはあまり意味がない。all や where-fun の中で funcall を呼ぶのも煩わしい。

実際、普通は上のようなコードは書かないだろう。不自然で不自由で、抽象化過剰のように感じられるからだ。この程度の内容ならば、一切の抽象化を放棄して単純なループで書いてしまう気がする。

function where() {
    var fields = arguments;
    return function(cd) {
	for(var i=0; i<fields.length; i+=2) {
	    if(cd[fields[i]] != fields[i+1])
		return false;
	}
	return true;
    }
}

しかしこの手の妥協を繰り返していると、やがてテストするのがしんどくなってくる。どこまで抽象化すべきか、どこで抽象化を諦めるか、特に再利用可能なライブラリを作っているときにはかなり悩ましい問題となる。

Lispではそういう妥協は不要ということだろうか。今後の内容に期待しよう。

Leave a Reply