通常、バイトコンパイラはコンパイルするファイルのフォームを実際には
実行しない。たとえば、ファイルが(setq foo t)
を含む場合、
コンパイルすることは実際にはt
をfoo
に設定しない。
setq
がトップレベルフォーム(すなわちdefun
または他の
フォームに囲まれていない)だったとしてもそれは真である。しかし、ある
トップレベルフォームをコンパイル時に評価させたい場合がある。たとえば、
ファイルの残りの部分が定義されたマクロを参照できるように、コンパイラは
defmacro
フォームをコンパイル時に実際に評価する。
このフォームは本体formsがいつ評価されるかを制御する。
situationsリストはシンボルcompile
、load
、そして
eval
(または長いANSIの同等物、:compile-toplevel
、
:load-toplevel
、そして:execute
)のいずれの集合を
含んでもよい。
eval-when
フォームは、トップレベルフォームとして
コンパイルされているかどうかによって異なって扱われる。つまり、
byte-compile-file
のようにファイルやコードのバッファを
コンパイルするコマンドによってコンパイルされているときに特別に扱われ、
文字通りファイルのトップレベルかprogn
の内部のトップレベルに
現れる。
コンパイルされたトップレベルのeval-when
には、compile
が
situationsリストにある場合には、forms本体はコンパイル時に
実行され、load
がsituationsリストにある場合には、
formsは(ロード時に実行されるように)ファイルに書かれる。
コンパイルされていないトップレベルフォームには、状況eval
のみが
関係する。(これはインタプリタが実行するフォーム、
byte-compile-file
ではなくbyte-compile
でコンパイルされた
フォーム、トップレベルではないフォームを含む。 ) eval
が
指定された場合、eval-when
はprogn
のように振る舞い、
そうでない場合はnil
(forms本体を無視する)のように
振る舞う。
eval-when
が入れ子の場合、規則はさらに微妙になる;
ぞっとするような詳細(そしてぞっとするような例)はSteele(第2版)を
調べること。
単純な例:
;; top-level forms in foo.el: (eval-when (compile) (setq foo1 'bar)) (eval-when (load) (setq foo2 'bar)) (eval-when (compile load) (setq foo3 'bar)) (eval-when (eval) (setq foo4 'bar)) (eval-when (eval compile) (setq foo5 'bar)) (eval-when (eval load) (setq foo6 'bar)) (eval-when (eval compile load) (setq foo7 'bar))
foo.elがコンパイルされると、これらの変数がコンパイル自身の間に設定される:
foo1 foo3 foo5 foo7 ; `compile'
foo.elcがロードされると、これらの変数が設定される:
foo2 foo3 foo6 foo7 ; `load'
ロードされたfoo.elがコンパイルされていないと、これらの変数が設定される:
foo4 foo5 foo6 foo7 ; `eval'
仮にこれら7個のeval-when
がdefun
内部にあれば、最初の3個は
nil
と同等であり、最後の4個は対応するsetq
と同等である。
(eval-when (load eval) …)
はあらゆるコンテキストで
(progn …)
と同等であることに注意せよ。コンパイラは
defmacro
(多少)やrequire
のようないくつかの
トップレベルフォームを(eval-when (compile load eval) …)
で
包まれているかのように扱う。
Emacs 19はeval-when
と関係がある2個の特殊形式を含む。そのうちの
1個であるeval-when-compile
は、どのeval-when
構文要素と
完全に同等ではなく以下に記述される。このパッケージはEmacs 18
ユーザのためにeval-when-compile
の1個の版を定義する。
他方のフォーム(eval-and-compile …)
は
‘(eval-when (compile load eval) …)’と正確に同等なので、それ
自身はこのパッケージで定義されていない。
formsはコンパイル時に評価される;実行時、このフォームは結果として
生じる値の引用された定数のように振る舞う。トップレベルで使われると、
eval-when-compile
はちょうど‘eval-when (compile eval)’
のようである。他のコンテキストでは、eval-when-compile
は効率
その他の理由でコードがコンパイル時に1度だけ評価されることを許す。
このフォームは真のCommon Lispの‘#.’文法に類似している。
formはロード時に評価される; 実行時、このフォームは結果として 生じる値の引用された定数のように振る舞う。
初期のCommon Lispはこれに類似した‘#,’文法を持っていたが、ANSI
Common Lispはload-time-value
で置き換えてよりよく定義された
意味論を与えた。
コンパイルされたファイルでは、load-time-value
はformが
.elcファイルがロードされたときに評価され引用された定数だったか
のように使われるように準備する。byte-compile-file
ではなく
byte-compile
でコンパイルされたコードでは、効果は
eval-when-compile
と同一である。コンパイルされていないコードでは、
eval-when-compile
とload-time-value
は正確にprogn
の
ように振る舞う。
(defun report () (insert "This function was executed on: " (current-time-string) ", compiled on: " (eval-when-compile (current-time-string)) ;; or '#.(current-time-string) in real Common Lisp ", and loaded on: " (load-time-value (current-time-string))))
バイトコンパイルされると、上のdefunの例は下記のコード (またはもちろんそのコンパイルされた同等のもの)を.elcファイルに 生じる:
(setq --temp-- (current-time-string)) (defun report () (insert "This function was executed on: " (current-time-string) ", compiled on: " '"Wed Jun 23 18:33:43 1993" ", and loaded on: " --temp--))