zick pages

EVALQUOTEの話

動機

「LISPといえばS式だが、昔はM式というものがあった」 という話はなぜか多くの人が知っている [要出典] 。 しかしM式は人間が読み書きするものであり、計算機が読み書きするものではない (APPLE-LISPなどM式を読めるLISPも一応存在する)。 昔の計算機のためのLISPプログラムの記述法を知る人は少ない [要出典] 。 具体的には吸血鬼関数の話を読んだ人から 「(このプログラムって)S式じゃないんですか?」という質問がやってきた。 そんなわけで昔のLISPプログラムの記述法の話をする。

実例

今どきのLispにおける (car '(a b c)) は、 昔のLISPだと CAR ((A B C)) のように書いた。 これだけだとよく分からないのでもっと例をあげる。

(cons 1 2)  ;; 今
CONS (1 2)  ;; 昔

(cons 'a 'b)  ;; 今
CONS (A B)    ;; 昔

(cons 'a '(b))  ;; 今
CONS (A (B))    ;; 昔

(car '(a b c))  ;; 今
CAR ((A B C))   ;; 昔

((lambda (x) (cons x x)) 1)  ;; 今
(LAMBDA (X) (CONS X X)) (1)  ;; 昔

((lambda (x) (cons 'x x)) 'a)        ;; 今
(LAMBDA (X) (CONS (QUOTE X) X)) (A)  ;; 昔

((lambda (x) (cons (car x) x)) '(a b))  ;; 今
(LAMBDA (X) (CONS (CAR X) X)) ((A B))   ;; 昔

最初の CONS (1 2) から想像が付くように、 今どきのLispだと (関数 引数1 引数2) と書くところを、 昔のLISPは 関数 (引数1 引数2) と書いた。 次の CONS (A B) から推測できるが、引数は勝手にクォートされる。 リストも当然クォートされるので CAR ((A B C)) のような書き方になる。 CAR (A B C) と書くとCARに3つの引数を渡すことになってしまうので注意。 LAMBDAが出てくると少し面白くなってくる。基本は同じで ((LAMBDA ...) 引数1 引数2)(LAMBDA ...) (引数1 引数2) となる。 面白いのはLAMBDAの内側だ。内側に関しては今と全く同じ記法になる。 具体的には (関数 引数1 引数2) という記法で、 引数が勝手にクォートされることはない。

正体

この古い記法を使うLISPはEvalquote Lispと呼ばれる。 今どきの記法を使うLispはEval Lispと呼ばれる。 Eval Lispは入力した式がevalによって評価される。 そこから想像できるように、 Evalquote Lispは入力した式がevalquoteによって評価される。

evalquoteの定義はLISP 1.5 Programmer’s Manualでは次のように書かれている。

evalquote[fn;x] = apply[fn;x;NIL]

EVALQUOTEとはAPPLYである

ちなみにAPPLYの第3引数は環境を表す連想リストだ。 今どきのLispにはないことを考えると、evalquoteは完全にapplyだと言える。

今どきの記法はevalに合わせたもの、古い記法はapplyに合わせたものと考えると、 すべて納得がいくだろう。 関数と引数を分けて書くのは当然。 引数がクォートされるというのも納得がいく。 applyは受け取った引数を評価したりしないからだ。

理由

しかし、なぜevalquoteなどという関数を導入したのだろうか。 具体的な説明を見つけることはできなかったが、 LISP I Programmer’s Manualを読めば少し想像ができる。 LISP IはEval LispでもなければEvalquote Lispでもない。 強いて名前をつけるならApply Lispだろう。 LISP Iでは関数と引数と環境の3つ組を入力する。 例えば CAR ((A B C)) () のように書く。 3つ組はそのままAPPLYに渡される。 LISP I Programmer’s Manualには “The APPLY operator is the interpreter part of the LISP system” とはっきりと書かれている。昔のLISPはevalではなくapplyが主役だったのだ。

しかしながらグローバル変数があれば環境が役に立つ場面はあまりなさそうだ。 そう考えると毎回NILを入力するのもバカバカしいし、 evalquoteというものが導入されたのも分からなくもない。

将来

LISP Iは関数と引数と環境の3つ組を受け取った。 LISP 1.5は関数と引数の2つ組を受け取った。 そして今どきのLispは式のみ(1つ組)を受け取る。 そう考えると将来のLispは何も受け取らないと考えるのが自然だろう。 対話環境でLispが対話を拒み一方的に話し続けると考えると面白そうだ。 詳細な設計は読者の練習問題とする。

2022-06-24