For Development HEAD DRAFTSearch (procedure/syntax/module):

7.4 ジェネリックファンクションとメソッド

メソッドの定義

Macro: define-generic name :key class

ジェネリック関数を生成し、name に束縛します。

通常は、これを使う必要はありません。もし、まだ存在していなければ、 define-method マクロが暗黙裏にジェネリック関数を生成してくれるからです。

キーワード引数classに、<generic>のサブクラスを渡すことで、 作られるジェネリック関数をデフォルトの<generic>クラスのインスタンスでは なく指定のクラスのインスタンスにすることができます。<generic>の サブクラスを定義してメソッド適用をカスタマイズする場合に便利です。

Macro: define-method name [qualifier …] specs body

name という名前のメソッドを定義します。すでにグローバルに name に束縛されているジェネリック関数オブジェクトが存在していれば、生成された メソッドはそのジェネリック関数に追加されます。name が未束縛であるか またはジェネリック関数以外に束縛されているなら、新しいジェネリック関数が 生成され、name に束縛されて、新しいメソッドがそれに追加されます。

名前の後に、省略可能なqualifierを置くことができます。 各qualifierはキーワードです。 現在、次のqualifierのみが有効です。

:locked

同じ特定化子(specs)を持つメソッドを再定義しないことを宣言します。 再定義しようとするとエラーが投げられます。 (特定化子の異なるメソッドは定義できます)。

組み込みオブジェクトの基本的な操作に関わるメソッドの多くはロックされています。 それらを再定義してしまうと、Gaucheの基盤が不安定になる恐れがあるからです。 また、最適化が可能になります。

specs はこのメソッドに対応する引数とその型を指定します。これは lambda 形式の引数リストに似ていますが、それぞれの引数の型を指定できる ところが違います。

specs : ( arg ... )
      | ( arg ... . symbol )
      | ( arg ... extended-spec ...)
      | symbol

arg   : ( symbol class )
      | symbol

class は引数が所属すべきクラスを指定します。arg が単に シンボルであれば、(arg <top>) と同じです。rest 引数の 型を指定することはできません。それは常にリストに束縛されるからです。

:optional:key:rest等の拡張引数指定を使うことも できます。(拡張引数指定については手続きを作るを参照してください)。 拡張引数指定は、メソッドディスパッチに関しては rest引数と同様に扱われます。 すなわち、省略可能引数やキーワード引数にクラスを指定することはできません。

引数リストのクラスのリストはメソッド特定化子リストといい、 これを基に、ジェネリック関数は適切なメソッドを選択します。specs と それに対応する特定化子リストの例をあげておきます。 (rest引数は特定化子リストには考慮されないことに注意。リストに決まっているからです。) optionalの項はメソッドがrest引数を取るかどうかを示します。

specs:        ((self <myclass>) (index <integer>) value)
specializers: (<myclass> <integer> <top>)
optional:     #f

specs:        (obj (attr <string>))
specializers: (<top> <string>)
optional:     #f

specs:        ((self <myclass>) obj . options)
specializers: (<myclass> <top>)
optional:     #t

specs:        ((self <myclass>) obj :optional (a 0) (b 1) :key (c 2))
specializers: (<myclass> <top>)
optional:     #t

specs:        args
specializers: ()
optional:     #t

その特定化子リストがジェネリック関数の中のメソッドの一つに一致し、 rest引数の有無も同じである name 上のメソッドを定義すると、既存のメソッドは新しく定義された メソッドに置き換えられます。(既存のメソッドがロックされていない限り)。

註: Gaucheがキーワード-シンボル統合モード ((キーワードとシンボルの統合参照)で実行されている場合、 specsが単独のシンボルで、それがキーワードでもあった場合 (引数すべてを単一の変数で受け取る、その変数がキーワードでもあった場合) に曖昧性が生じます。Gaucheではnameに続くキーワードはすべて qualifierとしてパーズするので、単一の変数からなるspecsには キーワードを使わないようにしてください。

ジェネリック関数の適用

ジェネリック関数は適用されると、まず、与えられた引数に適合する 特定化子リストを持つメソッドを選択します。たとえば、ジェネリック関数 foo が 3つのメソッドを持っており、それらの特定化子リストが それぞれ、(<string> <top>)(<string> <string>)(<top> <top>) であるとします。foo(foo "abc" 3) の ように適用されたとき、最初と 3番目のメソッドが選択されます。

選択されたメソッドは、もっとも特定化されたものから、もっとも一般的なものへ の順でソートされます。これは以下のように計算されます。

メソッドがソートされたら、最初のメソッドの本体が実引数で呼ばれます。

メソッド本体内部では、特別なローカル変数 next-methodが暗黙裏に 束縛されます。

Next method: next-method
Next method: next-method args …

この変数は、メソッド本体内部で、ソートされたメソッドリストで次のメソッド をカプセル化した特別なオブジェクトに束縛されます。

引数なしで呼ぶと、次のメソッドがこのメソッドよばれたときとと同じ引数で 起動されます。args … を明示的にわたすと、次のメソッドがその 渡された引数で起動されます。

next-method がもっとも特定化されていないメソッドで呼ばれた場合、 すなわち、「次のメソッド」がない場合、エラーシグナルがあがります。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT