#:g1: Lisp₂とFUNCALL (2)

Posted 2021-12-30 21:24:08 GMT

Franz Lispの関数呼び出しの作法についてのツイートですが、今の感覚からすると確かに奇妙です。

bitの記事からの引用ですが、

Franz Lispのように、関数名のところが変数であれば、その変数の値を関数と思い、それがまた変数であれば今度はその変数の値を関数名だと思い、…といったことはしない

とあります。実はこれMACLISPと同じような挙動なのですが、昔にこのブログに記事を書いていたことを思い出したのでMACLISPと同じ挙動なのか確認してみました。

処理系は、simhのvax ultrix 4.0 上の franz lispです。

$ lisp
Franz Lisp, Opus 38.79

-> (setq x '(0 1 2 3)) (0 1 2 3)

-> (defun foo (x) x) foo

-> ((lambda (car) (car x)) 'foo) 0

carの大域関数定義が優先されるようです。

-> ((lambda (bar) (bar x)) 'foo)
(0 1 2 3)

しかし関数定義がなければ、変数の方を使います。

barの関数を定義すれば、そちらが優先されます。

-> (defun bar (x) (car x))
bar

-> ((lambda (bar) (bar x)) 'foo) 0

しかし、MACLISPと違って、car部に置くことができるのは、シンボルだけのようです。

-> ('car '(0 1 2))
Error: eval: Undefined function  'car

-> (#'car '(0 1 2 3)) Error: eval: Undefined function (function car)

ちなみに、MACLISPと同様にこの挙動はインタプリタだけで、ファイルをコンパイルした場合は、エラーになります。

;; foo.lisp
(defun foo (x)
  (let ((kar #'car))
    (kar x)))

$ liszt foo.lisp
Compilation begins with Liszt vax version 8.36
source: foo.lisp, result: foo.lisp.o
foo
%Note: foo.lisp: Compilation complete
%Note: foo.lisp:  Time: Real: 0:0, CPU: 0:0.00, GC: 0:0.00 for 0 gcs
%Note: foo.lisp: Assembly begins
%Note: foo.lisp: Assembly completed successfully

$ lisp Franz Lisp, Opus 38.79 -> (load "foo.lisp.o") [fasl foo.lisp.o] t -> (foo '(0 1 2 3 4)) Error: Undefined function called from compiled code kar

まとめ

ここまでインタプリタとコンパイラの挙動が違うと統一したくなる気持ちもわかります。

ちなみに、Lisp₁とLisp₂は名前空間の違いとして語られることが殆どですが、上記の実験からも分かるように、実は同じLisp₂でもフォームのcar部の評価方法も様々なものがあります(した)。
Lisp₁の

(car '(0 1 2 3))

をそのままLisp₂にうつすと

('car '(0 1 2 3))

となる筈です。
しかし、Common Lispではcar部は評価しないものとして定められているので、

(car '(0 1 2 3))

としか書くことができないこともあり、多分細かいことは色々スルーされているのでしょう。

MACLISPやFranz Lispのインタプリタではcar部も関数が出てくるまで評価する、ということなのですが、この辺りも私がCommon Lispの関数/マクロ定義は括弧を定義しているのに感覚として近いと考える所以です。

関連


HTML generated by 3bmd in LispWorks 8.0.0

comments powered by Disqus