古き良きLambdaExpression

lambda式を動的に生成したい場合を考える。

EmacsLispでは

lambda式の形をしたリストはそのまま関数として使用可能なので

(funcall (list 'lambda '(x) '(+ x 3))
         3)
;=> 6

とすれば良い。

しかしCommonLisp(Cltl2)では

a list whose car is lambda is not, properly speaking, of type function

(2.13. Functionsより)
なので同じやり方はエラーになる。代わりに

(funcall (eval (list 'lambda '(x) '(+ x 3)))
         3)
(funcall (compile nil (list 'lambda '(x) '(+ x 3)))
         3)

とすれば同じことができる。(evalやcompile、#.リーダマクロ以外を使う方法は標準の範囲では多分ない。あったら是非教えてください。)

この理由はおそらく以下のとおり。

  1. EmacsLispの関数は「引数」と「定義本体」が全てなのでlambda式で関数の全てを表現できるのに対し、CommonLispの関数はさらに「環境」を保持するのでlambda式では関数の全てを表現できない。
  2. CommonLispは性能を重視するので、funcallがいちいち第一引数がリストなのか関数なのか調べなくて良いように、リストを関数から除外した。

さびしい

「carがlambdaで、cadrが引数で、caddrが定義本体のリスト」=関数という単純明快なルールが捨てられたのが寂しい。すこし裏切られた気分だ。

そういえばClojure、テメーもだ。

Clojureでは

(defn hoge [a b]
   (let [c (* a b)]
      (+ a b c)))

のように関数の仮引数リストやletの束縛リストには、角括弧"[]"を書いてベクトルを使わないといけない。しかも、丸括弧だとエラーになる。角括弧はリーダーによって自動的にリストに変換されるわけでもない。なんだそれは!!!。

追記(2010/9/18):
束縛リストにベクトルのみ使用可能という仕様は、「リストとアトムの二種類からなるコードに対してプログラムの意味が定められている」という慣習を破っているので。この慣習があるとマクロの利用やコードの走査が容易になるはずです。