スロットオプション(Slot Options)

クラス定義で許されるslot-specsは3つある。スロット指定(slot specifications), 初期化引数指定(initialization argument specifications)継承スロット指定 (inherited slot specifications)である。

スロット指定(Slot Specifications)

スロット指定はスロットを記述する。

スロット指定の構文は次の通りである。

[ adjectives ] [ allocation ] slot getter-name [:: type ] #key setter init-keyword required-init-keyword init-value init-function

getter-nameはスロットの取得子名(getter name)を指定する。これは変数名でなければならない。

adjectivesは空白で区切られた語である。現在のところ、許されている adjectivesopensealedである。 これらのadjectivesはこのスロットの取得子と (もしあれば)設定子がどの程度動的であるかを制御する。詳細は ダイナミズムの制御(Controlling Dynamism)の章を見よ。

allocation はスロットの割り付けを制御する。詳細は 割り付けの指定(Specifying Allocation)の項を見よ。

キーワードsetter, init-keyword, init-valueおよびinit-functionは それぞれ設定子の名前、初期化キーワード、そして(もし割り付けが仮想(virtual)でなかったなら) init-value:または init-function:で指定されるデフォルト値を指定する。 詳細は、 スロット指定で許されるキーワード(Keywords allowed in slot-specs)の項 を見よ。

もしinit-keyword:またはrequired-init-keyword:で指定される初期化キーワードがあるなら、 そのスロットはキーワード初期化可能(keyword initializable) であると言う。

次の例は総称関数bar-xbar-yによりアクセスされる、2つの、 キーワード初期化可能なスロットを持つクラスを定義している。


define class <bar> (<object>)
  slot bar-x, init-keyword: x:;
  slot bar-y, init-keyword: y:;
end class <bar>;

この例ではmake (<bar>, x: 2, y: 3)と言う呼び出しにより、これらの スロットが23にそれぞれ初期化されたインスタンスが作られる。 make (<bar>)と言う呼び出しではこれらのスロットが初期化されていない インスタンスが作られる。(取得関数により値を得ようとするとエラーになる。)

初期化引数指定(Initialization Argument Specifications)

初期化引数指定はスロットを記述するわけではなく、特定の初期化引数がどのように 扱われるかを記述する。初期化引数の型の制限、必須宣言、必須でない初期化引数の デフォルト値の指定を行うことができる。

初期化引数指定には2つの種類がある。すなわち、必須(required)初期化引数指定と 付加的(optional)初期化引数指定である。required

必須初期化引数指定は次のような形をしている。

required keyword keyword #key type

必須初期化引数は、その初期化引数がmakeを呼び出す際に必須であることを主張している。 デフォルトのmakeメソッドは、そのような初期化引数が与えられないとエラーを発信する。

付加的初期化引数指定は次のような形をしている。

keyword keyword #key type init-value init-function

付加的初期化引数指定は初期化引数のデフォルト値を定めるのに用いることができる。 makeの呼び出しがキーワードを指定しない場合、デフォルト値はデフォルトの makeメソッドがdefaulted initialization argumentsを計算し、 間接的にスロットを初期化するのに用いられる。(スロット初期化の詳細については 次の節を見よ。)

型引数はどちらの初期化引数指定でも同じ意味を持つ。すなわち、初期化引数の型を制限する。 (これはスロットの型を制限するのとは異なることに注意。)

次の例は前の例のクラス<bar>と似たクラスを定めるが、初期化の仕方が変更されている。

define class <baz> (<bar>)
   required keyword x:;
   keyword y:, init-value: #t;
end class <baz>;

初期化引数x:は必須であると指定されている。もしx:キーワード引数が与えられないと makeはエラーを発信する。これにより初期化引数y:、したがってまたbar-y スロットの初期値は、もしmakeの呼び出しで指定されなければデフォルトで#tとなる。

同じ初期化引数により複数のキーワード初期化スロットを初期化してもよい。(すなわち、複数の キーワード初期化可能なスロットが同じinit-keywordを指定してもよい。) しかしながら、単一のdefine-class式が同じキーワードに対して複数の初期化引数指定を 持つなら、エラーとなる。単一のdefine-class式に次のようなキーワード初期化可能スロット があると、やはりエラーとなる。すなわち、init-value:またはinit-function:を 指定している一方で、同じキーワードに対する初期化引数指定で値を要求する (required-init-keyword:)か、init-value:またはinit-function: によりデフォルト値を提供するようなものを指定している。これはつまり、スロットに対する その初期値指定が決して使われないような場合である。

継承スロット指定(Inherited Slot Specifications)

継承スロット指定は取得総称関数と、さらに付加的にinit-value:または init-function:スロットオプションのどちらか一方を定める。

継承スロット指定の構文は次の通り。

inherited slot getter-name #key init-value init-function

getter-nameはスロットを同定する。これは変数名でなければならない。 定義中のクラスの上位クラスがスロットを同じ取得子で指定していなければならない。

もしも init-value:init-function:のどちらも指定されなかったなら、 継承スロット指定の唯一の機能は定義中のクラスのどれかの上位クラスで同一の取得子を持つ スロットが指定されていなければならないと要請することである。 おそらく、ドキュメンテーションとしても有用であろう。仮想スロットに対しては init-value:init-function:のスロットオプションは許されないので、 仮想スロットに対しては継承スロット指定で有効なのはこの形だけである。

init-value:init-function:のどちらかが指定されたときには、 上位クラスのスロットのinit-value:init-function:スロットオプションは 無視され、継承指定のオプションが代わりに用いられる。これにより継承スロットの init-value:init-function:スロットオプションが下位クラスで 置き換えられることが許され、スロットのデフォルト初期値を変更することができる。


define class <animal> (<object>)
  slot n-legs, init-value: 4;
end class;

define class <spider> (<animal>)
  inherited slot n-legs, init-value: 8;
end class;

割り付けの指定(Specifying Allocation)

スロット指定でのallocationはスロットの格納領域がどのように割り付けられるか を指定する。allocationinstance,class,each-subclass, virtualまたはconstantのいずれかのシンボルでなければならず、 そうでないならば、処理系依存の値であるかもしれない。この引数は評価されない。

instance
各インスタンスがスロットに対し独自の格納領域を持つことを示す。 これがデフォルトである。
class
そのクラスの直接、間接、すべてのインスタンスにより利用される格納領域が一つ しかないことを示す。すべてのインスタンスはそのスロットに対し単独の値を共有する。 一つのインスタンスで値が変更されたなら、すべてのインスタンスが新しい値を参照する。
each-subclass
そのクラスがそのスロットに対して一つの格納領域を持ち、そのクラスのすべての 直接インスタンスにより使用されることを示す。さらに、各部分クラスはそれぞれ そのスロットのための格納領域を持ち、直接インスタンスはこれを利用する。
constant
そのスロットには定数値が割り当てられることを示す。設定メソッドはない。 スロットの値は init-valueとして指定されなければならない。
virtual
そのスロットに領域が自動的には割り付けられないことを示す。割り付けが 仮想(virtual)なら、スロットから値を取り出し、格納するための取得、 設定総称関数のメソッドを定めるのはプログラマの責任である。Dylanは指定された 任意の取得子、設定子に対して総称関数の存在を保証するが、それらに メソッドを与えることはしない。
新しいインスタンスが作られるとき、仮想スロットの値は自動的には初期化されない。 必要な初期化はプログラマが行わなければならない。通常、これはinitializeの メソッドの中で行われる。仮想スロットの値は実行時に他の値から計算されることがよくあるので、 多くの仮想スロットは明示的な初期化を必要としない。

仮想スロットでのslot-initialized?プロトコルをサポートするには、そのスロットの 取得メソッドとプロトコルを共有するメソッドをslot-initialized?に定めなければならない。

スロット指定で許されるキーワード(Keywords allowed in slot-specs)

各キーワードは高々一度だけ指定でき、次に値が来なければならない。 処理系はさらにキーワード引数をつけ加えてもよい。

setter:
設定メソッドが付加されるモジュール変数の名前、または、設定メソッドを 定義すべきでないときには#fを与える。この引数は評価されない。 このキーワードが与えられないと、デフォルトはgetter-nameと"-setter"を つなげた名前のシンボルとなる。ただし、スロットの割り付けが constantの時は別で、デフォルトが#fとなる。

type:
スロットに格納可能な値の型を制限する型指定子を与えなければならない。 この型指定は低レベルのスロット格納機構により要請される。仮想スロットでは、 低レベルのスロット格納機構を用いないので、要請されない。 この引数のデフォルトは<object>であり、この場合スロットにどんな オブジェクトでも格納できる。引数は一度だけ評価される。それは、クラスを含む変数が 定義された後で、インスタンスにスロットが初めて加えられる前である。

init-value:
スロットのデフォルト初期値を与える。引数は一度だけ評価される。 それは、クラスを含む変数が定義された後で、インスタンスにスロットが初めて 加えられる前である。結果として得られる値はインスタンスが作られるときの 初期値として使われる。各インスタンス毎に新しい値を作りたいなら、 init-value:ではなくinit-function:を与えなければならない。 この引数にはデフォルト値はない。init-value:init-function: と一緒に与えないほうがよい。

init-function:
引数をとらない関数を与える。この関数は新しいインスタンスが作られるとき スロットの初期値を生成するのに呼ばれる。この引数にはデフォルト値はない。 引数は一度だけ評価される。 それは、クラスを含む変数が定義された後で、 インスタンスにスロットが初めて加えられる前である。 init-function:init-value:と一緒に与えないほうがよい。

init-keyword:
キーワードを与えなければならない。そのキーワードをキーワード引数として 用いて、スロットの初期値をmakeに渡すことができるようになる。 使い方は以下のスロット初期化の説明 を見よ。この引数は評価されない。 init-keyword:引数にはデフォルトはない。何も指定されないときには、 そのスロットにはinit-keyword がないことになる。 init-keyword:required-init-keyword:の両方を単一の スロットに対して指定することはできない。

required-init-keyword:
クラスがインスタンス化されるとき、提供されなければならない init-keywordを表している点を除いて、init-keyword:と同様である。 そのクラスに対してmakeが呼ばれ、必須のinit-keywordが提供されなかったなら、 エラーになる。 もしもrequired-init-keyword:がスロットに対し指定されているなら、 init-keyword:,init-value:およびinit-function:は 指定できない。

フィルターされたスロット(Filtered Slots)

スロットの値はあるクラスのインスタンスに直接格納されるが、その部分クラスでは 何か計算を必要とするような状況もある。例えば、positionスロットは <view>の直接インスタンスでは値として格納され得るだろうが、 <view>の部分クラス<displaced-view>の 直接インスタンスでは計算が必要となる。

どちらのクラスもposition(position総称関数)に対して同じ インターフェイスを提供しなければならない。もしもいずれかのクラスの作成者が、 実現法を変えると決定しても、クラスの使用者は変更を行ったり、コンパイルし直す必要はない。 インターフェイスには変更がないからである。 整合的な関数呼び出し構文は実現の詳細を隠す助けとなる。

この場合、<view>クラスはpositionをインスタンススロットとして サポートし、<displaced-view>クラスはpositionをフィルタされた スロットとしてサポートしている。positionposition-setterのメソッドは <view>内のスロット定義により自動的につけ加わる。これらのメソッドは インスタンスの値にじかにアクセスする。これに対して、<displaced-view>の 作成者は2つの総称関数に明示的にメソッドをつけ加えなければならない。 <displaced-view>メソッドはスロットに格納されている値にアクセス (取り出し、または格納)するためにnext-methodを呼ぶことができる。


define class <view> (<object>)
  instance slot position;
  ...
end class;

define class <displaced-view> (<view>)
  ...
end class;

define method position (v :: <displaced-view>)
  displace-transform (next-method (v));
end method;

define method position-setter (new-position),
                               v :: <displaced-view>)
  next-method (undisplace-transform (new-position), v);
end method;

一方、プログラマとしては、インスタンスにスロット値としての格納場所がほしいが、 スロットがアクセスされるたびに何か補助的な動作をさせたいという状況もあり得る。 このような場合、プログラマは2つのスロットを定めなければならない。 すなわち、格納領域を提供するインスタンススロットとインターフェイスを提供する 仮想スロットである。一般に、仮想スロットのみがドキュメント化される。 インスタンススロットは仮想スロットにより格納のため使用される内部実現である。 このような使用の例は値をキャッシュするスロットである。

define class <shape> (<view>)
  virtual slot image;
  instance slot cached-image, init-value: #f;
  ...
end class;

define method image (shape :: <shape>)
  cached-image (shape)
    | (cached-image (shape) := compute-image (shape));
end method;

define method image-setter (new-image, shape :: <shape>)
  cached-image (shape) := new-image;
end method;

上の例のように、仮想スロットを定義したなら、スロットアクセス関数のための 内部インスタンススロットを定めることが予期されていることに注意せよ。
目次 索引 クラス(章見出し)、 インスタンスの生成(次節)