つうさにメモブログ

つうさにがメモをブログとして書いていくところ

eval-when-compileとeval-and-compileの違い

Emacs lisp コンパイラの気持ちになるとわかります。

eval-when-compileeval-and-compile

似た名前のマクロだが、どういう動作をして、何が違うのかメモ。

定義

なんとびっくり、全く同じ定義。動作はprognとほぼ同じ。

なんだ違う名前だけど、動作同じなのか〜ってそんなわけはない。

正確に言うと、Emacs lispインタプリタでは全く同じに解釈されるが、Emacs lispバイトコードへのコンパイルで違いがある。

Emacs lispバイトコードコンパイラでの評価

github.com

上に示すように、byte-compile-initial-macro-environment変数にはEmacs lispインタプリタでの動作とEmacs lispバイトコードコンパイラでの動作が違うマクロが書かれている。

Emacs lispバイトコードコンパイラは、基本的にelispを評価することはせず、ソースコードバイトコードへ変換(コンパイル)する。 しかし、eval-when-compileeval-and-compileで囲んだ式はコンパイル中に評価される。

そして2つには違いがある。

実はrequireは、Emacs lispバイトコードコンパイラによってeval-and-compile同等の処理がなされる(マニュアルより)。

実例

以下のようなeval-foo-compile.elファイルを用いて試す。

(eval-when-compile
  (princ "eval-when-compile\n"))
(eval-and-compile
  (princ "eval-and-compile\n"))

コマンドラインから

  1. Emacs lispインタプリタで評価
  2. Emacs lispバイトコードコンパイラコンパイル
  3. Emacs lispバイトコードインタプリタで出力されたバイトコードを評価

する。

$ emacs --script eval-foo-compile.el
eval-when-compile
eval-and-compile
$ emacs -batch -f batch-byte-compile eval-foo-compile.el
eval-when-compile
eval-and-compile
$ emacs --script eval-foo-compile.elc
eval-and-compile
$

期待した通りの結果になっている。

バイトコードの中身

Emacs lispバイトコードファイルは、Javaなどとは違って、全部バイナリというわけでない。

出力されたバイトコードeval-foo-compile.elcは以下のようになった。

;ELC^W^@^@^@
;;; Compiled
;;; in Emacs version 26.2
;;; with all optimizations.

;;; This file uses dynamic docstrings, first added in Emacs 19.29.

;;; This file does not contain utf-8 non-ASCII characters,
;;; and so can be loaded in Emacs versions earlier than 23.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


(princ "eval-and-compile\n")

今回はバイナリ列が一切出てこなかった。eval-when-compileで囲んだ式は出力ファイルに現れないことがわかる。

終わり

バイトコードって考え方自体あやふやだったので、きっちり調べられてよかった。

元のコードのインタプリタバイトコードコンパイラ/インタプリタがある言語って他にあるんだろうか。

Emacs lispもWrite once, run anywhereな感じで、多分30億のデバイスで動くと思うんだけど、バイトコードファイルだけで配布とか全くないのは自由ソフトウェアの文化だからなんだろうか(?)。

プログラミング処理系知識メモ