COMMON LISP JP > Archives > 2013/05/11

2013/05/11 13:29:04 UTCiyanaha
#
どなたかCommon Lispの特殊オペレーターEVAL-WHENについて教えてくださいませんか。
#
PCLの原著に当たってみたり、CLHSにあたってみたりしたんですが、さっぱりわかりません。
#
具体的なコードとともに説明を下さるとうれしいです。
2013/05/11 13:40:19 UTCmkamotsu
#
Lispのコードはload→compile→executeされるわけですが,(eval-when (:load-toplevel :compile-toplevel :execute) ...)なコードは可能な限り早い段階で評価されます.
2013/05/11 14:06:15 UTCg000001
#
EVAL-WHENは、評価(eval)するフェーズ(when)を指定するものですが、指定したフェーズでボディ内が評価されます。
#
具体例としては、マクロがマクロ展開で使う補助関数がマクロ展開時に定義されてないとマクロを展開するのに困るのを解決する、というのが良くみかける簡単な例かなと思います
2013/05/11 14:07:33 UTCllibra
#
(ql:quickload :iterate)

(defun integers (start end)
  (iter (for i from start to end) (collect i)))
#
この場合、コンパイル時にiterマクロの定義が分からずエラーになりますが、
#
(eval-when (:compile-toplevel)
  (ql:quickload :iterate)
  (use-package :iter))

(defun integers (start end)
  (iter (for i from start to end) (collect i)))
#
コンパイル時に定義を読み込むことで、コンパイルできるようになります。
2013/05/11 14:09:09 UTCg000001
#
(defun rev (x)
  (reverse x))

(defmacro foo (&body body)
  (cons 'progn (rev body)))
#
みたいな定義が同じファイルにある場合、defunで定義されたrevが実行できるようになるのは、ロードの後ということになっているので、コンパイル→ロードの場合、コンパイル時にfooのマクロ展開でrevが使えないことになってしまいます
#
ので、コンパイル時に評価されて欲しいrevの定義に
#
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun rev (x)
    (reverse x)))

(defmacro foo (&body body)
  (cons 'progn (rev body)))
#
と付けることになります。
#
ちなみに、この場合についてはrevの定義とfooの定義のファイルを分けて、revのファイルを先に読めば回避できます。
#
あ、読まないで書いてたらllibraさんと被ったw
2013/05/11 14:17:23 UTCllibra
#
申し訳ない。被ったので続きを書くのは中断しました。
2013/05/11 14:22:04 UTCg000001
#
いえ、すいません、今REPLから書き込むのを試してて、書き込むだけでレス読んでませんでしたw
2013/05/11 14:28:11 UTCllibra
#
再開します。上で言及されているような、コンパイル時などの特定のフェーズ(タイミング)で必要な処理を書くために利用する、というのが自分の理解です。Common Lispの実行モデルについて把握していないとピンと来ないかもしれません。
#
あと、自分の例の前者でuse-packageが抜けてるので、頭の中で補完していただけると幸いです。以上です。
2013/05/11 15:03:45 UTCg000001
#
http://d.hatena.ne.jp/bowbow99/20110116/1295141726 と同じような実験をしてみるのもeval-whenの理解には良いかもしれないですね。
#
複数ファイルのプロジェクトを作成して、どうもファイルの中の式が評価されたりしなかったりするようで、困るという時に思い出して試してみる感じで良いのではないでしょうか。