Standard Generic Function MAKE-LOAD-FORM
make-load-form object &optional environment => creation-form[, initialization-form]
make-load-form (object standard-object) &optional environment
make-load-form (object structure-object) &optional environment
make-load-form (object condition) &optional environment
make-load-form (object class) &optional environment
object - オブジェクト
environment - 環境オブジェクト
creation-form - フォーム
initialization-form - フォーム
ジェネリック関数make-load-formは、 ひとつか2つのフォームを返却します。 それぞれcreation-formとinitialization-formであり、 load時にobjectと等価なオブジェクトを構築することができます。 environmentは、フォームが処理されるときの レキシカルな環境に対応する環境オブジェクトです。
ファイルコンパイラは、特定のクラスのリテラルオブジェクトを処理するため、 make-load-formを呼び出します。 3.2.4.4. 外部オブジェクトの制約の追記を参照。
仕様に適合したプログラムは、objectがstandard-object, structure-object, conditionからなる一般的なインスタンスであれば、 make-load-formを直接呼び出します。
creation-formはload時に評価されるフォームであり、 objectと等価なオブジェクトを返却しなければなりません。 等価の正確な意味はobjectの型によって異なるので、 make-load-formのメソッドを定義するプログラマーに任されています。 3.2.4. ファイルコンパイル時のリテラルオブジェクトを参照。
もしobjectがinitialization-formに定数として現れたときは、 ロード時にそれは、creation-formによって生成されたオブジェクトと 同一なものとして置き換えられます。 これは、さらなる初期化処理でオブジェクトにアクセスする方法です。
creation-formとinitialization-formの両方で、 外部オブジェクトの参照が含まれる場合があります。 しかし、creation-formsでは循環的な依存関係はがあってはなりません。 循環的な依存関係とは、例えば オブジェクトXのcreation-formにオブジェクトYが含まれており、 オブジェクトYのcreation-formにオブジェクトXが 含まれているような場合です。 initialization-formには循環的な依存関係のような制約はありません。 これがinitialization-formフォームが存在する理由です。 循環的なデータ構造の例は下記を参照してください。
objectのcreation-formは、常にinitialization-formより前に実行されます。 creation-formまたはinitialization-formのいずれかが、 コンパイル中のファイル内で 以前に参照されていない他のオブジェクトを参照する場合、 コンパイラは、その参照しているフォームを評価する前に、 すべての参照されているオブジェクトが作成されたことを確認します。 参照されるオブジェクトが、ファイルコンパイラによって make-load-formを実行するタイプの場合は、 そのオブジェクトのために、 評価されたcreation-formの返却値を実行します。 (これが、creation-formで循環参照を禁止している理由です。)
各initialization-formは、データフローに従って、 そのcreation-form実行の後、できるだけ早く評価されます。 もしファイル内で、objectのinitialization-formが、 まだ参照されていない他のどんなオブジェクトも参照していないときに そのファイルコンパイラがmake-load-formを実行した場合は、 initialization-formはcreation-form処理直後に実行されます。
もしcreation-formかinitialization-formのフォームをFとしたとき、 Fがそのようなオブジェクトを参照を含む場合は、 それらのオブジェクトはcreation-formはFの前に実行され、 また、それらがFによって生成と初期化が行われるオブジェクトに依存しない限り、 それらのオブジェクトのinitialization-formもFの前に実行されます。 これらの規則によって、作成と初期化の2フォーム間で 評価する順序が一意に決定されない個所については、 その評価順序は決められていません。
これらの作成と初期化のフォームが評価されている間の オブジェクトはおそらく初期化されていない状態であり、 例えるなら、allocate-instanceで作成され、 initialize-instanceで完全に初期化処理されるまでの オブジェクトの状態と似ています。 make-load-formのメソッドを作成するプログラマーは、 まだ初期化されていないスロットに依存しないよう、 注意深くオブジェクトを操作しなければなりません。
loadがフォームをevalで処理するか、 あるいは同等の効果を持つ他のオペレーションを行うかは、 処理系に依存します。 例をあげると、フォームを別の違った同等の形式に変換して評価する、 それらをコンパイルした結果の関数をloadから呼ぶ、 それらがevalとは違う特殊な目的の関数によって実行される、 などがあります。 フォームを評価するということと同等の効果があればよいのです。
型classに特化したメソッドは、 もしクラスがちゃんとした名前を持っているときは、 クラス名を使ったcreation-formが返却されます。 もしクラスが名前を持っていない場合は、 型errorのエラーが発せられます。 creation-formの評価は、find-classを呼び出すように、 その名前を使用してクラスを検索します。 もしその名前のクラスが定義されていないときは、 クラスは実装で定義した方法で処理されます。 もしcreation-formを評価した結果、 クラスを返却できなかった場合は、 型errorのエラーが発生します。
仕様に適合した実装と仕様に適合したプログラムの両方が make-load-formをさらに特別化することができます。
(defclass obj ()
((x :initarg :x :reader obj-x)
(y :initarg :y :reader obj-y)
(dist :accessor obj-dist)))
=> #<STANDARD-CLASS OBJ 250020030>
(defmethod shared-initialize :after ((self obj) slot-names &rest keys)
(declare (ignore slot-names keys))
(unless (slot-boundp self 'dist)
(setf (obj-dist self)
(sqrt (+ (expt (obj-x self) 2) (expt (obj-y self) 2))))))
=> #<STANDARD-METHOD SHARED-INITIALIZE (:AFTER) (OBJ T) 26266714>
(defmethod make-load-form ((self obj) &optional environment)
(declare (ignore environment))
;; この定義は、XとYがオブジェクト自身を参照する情報を
;; 含んでいない場合にのみ有効であることに注意してください。
;; この問題に対するより一般的な解決策については、
;; 以下の改訂例を参照してください。
`(make-instance ',(class-of self)
:x ',(obj-x self) :y ',(obj-y self)))
=> #<STANDARD-METHOD MAKE-LOAD-FORM (OBJ) 26267532>
(setq obj1 (make-instance 'obj :x 3.0 :y 4.0)) => #<OBJ 26274136>
(obj-dist obj1) => 5.0
(make-load-form obj1) => (MAKE-INSTANCE 'OBJ :X '3.0 :Y '4.0)上記の例では、objの等価なインスタンスは、 そのスロットのうちの2つの値を使用して再構築されます。 3番目のスロットの値は、これら2つの値から導かれます。
make-load-formメソッドの別の書き方の例は、 make-load-form-saving-slotsを使うことです。 このメソッドが生成するコードは、 上に示したmake-load-formメソッドとは 少し異なる結果をもたらすかもしれませんが、 操作上の効果は同じです。 例を示します。
;; 上記で定義したメソッドを再定義する
(defmethod make-load-form ((self obj) &optional environment)
(make-load-form-saving-slots self
:slot-names '(x y)
:environment environment))
=> #<STANDARD-METHOD MAKE-LOAD-FORM (OBJ) 42755655>
;; 上記で作成したオブジェクトにMAKE-LOAD-FORMを実行
(make-load-form obj1)
=> (ALLOCATE-INSTANCE '#<STANDARD-CLASS OBJ 250020030>),
(PROGN
(SETF (SLOT-VALUE '#<OBJ 26274136> 'X) '3.0)
(SETF (SLOT-VALUE '#<OBJ 26274136> 'Y) '4.0)
(INITIALIZE-INSTANCE '#<OBJ 26274136>))次の例は、my-frobのインスタンスが、 何らかの方法でinternされています。 スロットnameの値をキーとして既存のオブジェクトを検索し、 同等のインスタンスを再構築しています。 この例では、プログラマーはオブジェクトが存在しなかったとき、 新しいをオブジェクトを生成することを選んでいますが、 他にはエラーを発生させるという場合もあります。
(defclass my-frob ()
((name :initarg :name :reader my-name)))
(defmethod make-load-form ((self my-frob) &optional environment)
(declare (ignore environment))
`(find-my-frob ',(my-name self) :if-does-not-exist :create))次の例では、各親がその子のリストを持ち、 各子がその親に戻る参照を持っているので、 データ構造は循環しています。 このような構造のひとつのオブジェクトにmake-load-formが呼ばれると、 creation-formは同等のオブジェクトを作成し、 childrenスロットを埋め、 その子や孫などすべてのオブジェクトに対して 同等のオブジェクトを強制的に作成します。 この時点では、parentスロットはひとつも埋まっていません。 initialization-formフォームでは、 parentスロットが埋められ、 親に相当するオブジェクトがまだ作成されていない場合は、 強制的に作成されます。 したがって、ロード時にツリー全体が再作成されます。 コンパイル時では、 make-load-formはツリー内の各オブジェクトに対して 一度だけ呼び出されます。 すべてのcreation-formが処理系に依存した順序で評価され、 次にすべてのinitialization-formが、 同じく処理系に依存した順序で評価されます。
(defclass tree-with-parent () ((parent :accessor tree-parent)
(children :initarg :children)))
(defmethod make-load-form ((x tree-with-parent) &optional environment)
(declare (ignore environment))
(values
;; creation form
`(make-instance ',(class-of x) :children ',(slot-value x 'children))
;; initialization form
`(setf (tree-parent ',x) ',(slot-value x 'parent))))次のの例では、データ構造に特別な特性はなく出力され、 単純にスロットの内容を再構築するだけで 同等の構造体を再構築することができます。
(defstruct my-struct a b c)
(defmethod make-load-form ((s my-struct) &optional environment)
(make-load-form-saving-slots s :environment environment))なし。
standard-object, structure-object, condition の特定メソッドは、型errorのエラーが発生します。
システムクラスの一般化されたインスタンスに対してmake-load-formを呼び出すと、 エラーが発生するか、作成・初期化フォームが返されるかは、処理系に依存します。
compile-file, make-load-form-saving-slots, 3.2.4.4. 外部オブジェクトの制約の追記, 3.1. 評価, 3.2. コンパイル
ファイルコンパイラは、特定の状況下でmake-load-formを呼び出します。 3.2.4.4. 外部オブジェクトの制約の追記を参照。
実装によっては、システムクラスとして指定されたクラスの新しいサブクラスを 定義する機能を提供する場合があります。 (考えられる候補としては、 generic-function、method、streamがあります。) そのような実装では、ファイルコンパイラがそのようなクラスのインスタンスを リテラルオブジェクトとして遭遇したときにどのように処理するかを文書化し、 make-load-formに関連するメソッドを文書化しなければなりません。