#:g1: Common Lispのeval-whenとコンパイラかインタプリタ実装かは別のレイヤーの話

Posted 2018-03-07 20:02:22 GMT

先日Twitterでこんなやりとりを目にしました

たしかに太古のMACLISPや、Franz Lispでは、変数の結合がダイナミックだったりして、コンパイラ(静的に決まることが多かった)かインタプリタ(基本動的)かで挙動が変わることが悩みのタネだったことがありました。

しかし……Common Lispではコンパイラかインタプリタ実装かで実行に差異はない

eval-whenというからにはLispが指しているのはCommon Lispだと思いますが、Common Lispでは、評価器がコンパイラかインタプリタ実装かで違いはでないこと、と規定されています。
また、プログラムがどちらの方式で実行されているか知る手立てもありません。

evaluation n. a model whereby forms are executed , returning zero or more values.
Such execution might be implemented directly in one step by an interpreter or in two
steps by first compiling the form and then executing the compiled code; this choice is
dependent both on context and the nature of the implementation, but in any case is
not in general detectable by any program. The evaluation model is designed in such a
way that a conforming implementation might legitimately have only a compiler and
no interpreter, or vice versa. See Section 3.1.2 (The Evaluation Model).

Common Lispがするコンパイルと実装戦略は別

じゃあ、Common Lispのcompileとかcompile-fileはどうなるんだ、と思うかもしれないですが、Common Lispが規定するコンパイルプロセスは、評価器の実装戦略とは別のレイヤーの話です。

以下、ややこしいのでコンパイルプロセスの方をコンパイルと書きます。
Minimal Compilationというのが定められていますが、インタプリタのみの処理系でも、コンパイルしてロードして、というのは可能で、その場合にはコンパイルによってマクロ展開等の前処理的なものを省いたものを解釈していくことになると思われます(多分)

また逆に、evalが即ちインタプリタということもなく、フォームの一塊のツリーをコンパイルしてから処理しても問題ありません。

Minimal compilation is defined as follows:
• All compiler macro calls appearing in the source code being compiled are expanded, if at
all, at compile time; they will not be expanded at run time.
• All macro and symbol macro calls appearing in the source code being compiled are
expanded at compile time in such a way that they will not be expanded again at run
time. macrolet and symbol-macrolet are effectively replaced by forms corresponding to
their bodies in which calls to macros are replaced by their expansions.
• The first argument in a load-time-value form in source code processed by compile
is evaluated at compile time; in source code processed by compile-file , the compiler
arranges for it to be evaluated at load time. In either case, the result of the evaluation is
remembered and used later as the value of the load-time-value form at execution time.

eval-whenとは

eval-whenは、主に処理が複数に分かれてしまう(ファイルをコンパイルしてロードする等)場合に生じる問題を解消するためのもので、コンパイル中にコンパイル中に定義した関数を使いたいとか、コンパイル済みのファイルをロードする場合に特定の仕事をさせたい等々のことに利用します。

Emacs Lispのeval-when-compileも似たようなものだと思いますが、脚注のコメントによると、Common Lispの#.(リード時評価)により近いそうです。

用途としてはCommon Lispと同じではないでしょうか。インタプリタ実装でもコンパイルは可能なので、Emacs Lispは、100%インタプリタ実装のCommon Lispに近い感じなのかもしれません。

他の方々からの指摘

上記で説明したように、挙動は変わないことになっています。

上記で説明したように、eval-whenと実装戦略は別のレイヤーで、これらの組み合わせがコードの意味を変えることはありません。

まとめ

Common Lispではコンパイラかインタプリタ実装かで実行に差異はない、と書きましたが、これは過去のLispでの問題を解決する歴史の上に成り立ったものでした。
Common Lispの歴史の中でも、ANSI規格以前のCLtL1では、*applyhook**evalhook*compiler-let等、コンパイラ/インタプリタ動作で整合性が取れなくなる/取るのが難しい機能が散見されましたが、これらはANSI規格として煮詰められる際に廃止となっていたりします。

また、現代のCSの意味論からすると不思議なこともあると思いますが、CSの意味論が洗練/確立する前からLispのコンパイラ/インタプリタは存在します。
長い歴史の中で、互いに影響しあったかもしれませんし、同じような結果になったとしても道程は全然違うものかもしれません。

コンパイラ……インタプリタ……コンパイラがコンパイル……と書いてる間に段々良く分からなくなってきたので、間違いがあったらご指摘よろしくお願いします。

ちなみに何故Twitterの議論には参加していないのかというと、自分は鍵アカウントの為で、ブログで失礼しました。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus