7章Objectsのジェネリック関数関連の和訳
【追記】7章Objectsの全てを日本語訳しました。
https://nptcl.github.io/npt-japanese/docs/ansicl/7.html
https://nptcl.github.io/npt-japanese/md/ansicl/7.html
ふたつのリンクは、レイアウトが違うだけでどちらも同じ内容なので好きな方を見てください。
リンク先はDictionaryも翻訳してます。
ただし翻訳してあるのは7章だけです。
7章Objectsのクラス関連の和訳の続きです。
目次
7.6 ジェネリック関数とメソッド 7.6.1 ジェネリック関数の紹介 7.6.2 メソッドの紹介 7.6.3 特定パラメーターと限定子の合致 7.6.4 ジェネリック関数の全てのメソッドのラムダリストの合意 7.6.5 ジェネリック関数とメソッドのキーワード引数 7.6.5.1 ジェネリック関数とメソッドのキーワード引数の例 7.6.6 メソッドの選択とコンビネーション 7.6.6.1 有効なメソッドの決定 7.6.6.1.1 適用可能なメソッドの選択 7.6.6.1.2 優先順位による適用可能なメソッドのソート 7.6.6.1.3 ソートされた適用可能なメソッドのMethod-Combination実行 7.6.6.2 Standard Method-Combination 7.6.6.3 Method-Combinationの宣言 7.6.6.4 組み込みのMethod-Combination 7.6.7 メソッドの継承
7. オブジェクト
7.1~7.5までは7章Objectsのクラス関連の和訳を参照。
7.6 ジェネリック関数とメソッド
7.6.1 ジェネリック関数の紹介
ジェネリック関数は、指定された引数のクラスか、 あるいは引数の同一性に依存して動作する関数です。 ジェネリック関数オブジェクトは、メソッドの集合、 ラムダリスト、method-combination、そしてその他の情報に関連付けられます。
通常の関数のように、ジェネリック関数は引数を取り、 一連のオペレーションを実行し、そしておそらくは有効な値を返却します。 通常の関数は単一のコードの実体を持ち、関数が呼び出されたときに常に実行されます。 ジェネリック関数はコードの実体を複数の集合として持ち、 その集合の一部か全部を関数実行のときに選択します。 選ばれたコードの集合とその組み合わせは、ジェネリック関数に渡される1つか複数の引数から、 クラスかあるいは同一性により決定します。それはmethod-combinationによって決定が行われます。
通常の関数とジェネリック関数は、同一の構文により呼び出されます。
ジェネリック関数は本物の関数なので、funcall
とapply
の最初の引数として使用されたり、
あるいは引数を渡したりすることができます。
ジェネリック関数の関数名の設定は、いくつかの手順のひとつとして確立します。
それはグローバル環境内において、ensure-generic-function
,
defmethod
(暗にensure-generic-function
が呼ばれる),
defgeneric
(これもまた暗に、ensure-generic-function
が呼ばれる)によって確立されます。
レキシカル環境において、ジェネリック関数の関数名の束縛を確立するための
標準的な方法は提供されていません。
defgeneric
フォームが評価されるとき、
(ensure-generic-function
によって)次の3つのうちの1つの手順が取られます。
もし指定した名前のジェネリック関数がもう存在している場合は、 存在しているジェネリック関数オブジェクトを修正します。 いま実行された
defgeneric
フォームによって宣言されたメソッドは追加され、 そして以前のdefgeneric
フォームによって定義された、 存在していたジェネリック関数のどんなメソッドも削除されます。 いま実行されたdefgeneric
フォームによるメソッドの追加は、defmethod
,defclass
,define-combination
, そしてdefstruct
によって 定義されたメソッドも置き換えます。 ジェネリック関数内のその他のメソッドについては、影響は無いですし置き換えもされません。もし指定した名前が、通常の関数、マクロ、特殊オペレーターによる名前であった場合は、 エラーが発せられます。
そうでなければ、ジェネリック関数は
defgeneric
フォーム内にある メソッド定義により宣言されたメソッドとともに作成されます。
いくつかのオペレーターは、ジェネリック関数のオプションの定義として、
使用するmethod-combinationのタイプや引数優先順位を指定することが許されています。
これらのオペレーターは、「ジェネリック関数のオプションを指定するオペレーター」と言います。
この分類の中で、標準的なオペレーターはdefgeneric
だけです。
いくつかのオペレーターは、ジェネリック関数のメソッドを定義します。 これらのオペレーターはメソッド定義オペレーターと言われます。 このオペレーターに関連付けられたフォームは、メソッド定義フォームと呼ばれます。 標準的なメソッド定義オペレーターを次の表に示します。
defgeneric define-combination defmethod defstruct defclass 表7-1 標準メソッド定義オペレーター
注意として、標準メソッド定義オペレーターのdefgeneric
だけは、
ジェネリック関数のオプションを指定することが出来ます。
defgeneric
といくつかの実装定義オペレーターは、
ジェネリック関数のオプションを指定することが可能であり、
「ジェネリック関数のオプションを指定するオペレーター」と言われます。
7.6.2 メソッドの紹介
メソッドは、クラス特定か、あるいは同一性特定による動作と、 ジェネリック関数のオペレーションを定義します。
メソッドオブジェクトは、次のものと結びつけられます。 それは、メソッドの動作を定義したコード。 与えられたメソッドが適用するかどうかを特定するための一連の特定パラメーター。 ラムダリスト。そして、method-combinationがメソッドの区分けをするために使われる、一連の限定子。
メソッドオブジェクトは関数ではないため、関数として呼び出すことはできません。 ジェネリック関数が呼び出されたときのような場合において、 オブジェクトシステムの様々な仕組みがメソッドオブジェクトを受け取り、 そしてメソッド関数を呼び出します。 このような動作を、メソッドが実行された、あるいはメソッドが呼び出されたと言います。
メソッド定義フォームは、 ジェネリック関数の引数起因により定義メソッドを実行するためのコードを含みます。 メソッド定義フォームが評価されたとき、メソッドオブジェクトは作成され、 次の4つのアクションが取られます。
もし指定した名前のジェネリック関数がすでに存在しており、 さらに特定パラメーターと限定子が 新しいものと一致するメソッドオブジェクトがすでに存在していた場合は、 新しいメソッドオブジェクトが古いものと置き換えられます。 定義されたメソッドが別の特定パラメーターと限定子の場合は、 7.6.3 特定パラメーターと限定子の合致を参照。
もし指定した名前のジェネリック関数がすでに存在しており、 さらに特定パラメーターと限定子が 新しいものと一致するメソッドオブジェクトが存在していなかった場合は、 既存のジェネリック関数オブジェクトは、新しいメソッドオブジェクトを含むように修正されます。
もし指定した名前が、通常の関数か、マクロ、特別オペレーターの名前であった場合は、 エラーが発せられます。
それ以外の場合は、メソッド定義フォームによって定義されたメソッドとともに、 ジェネリック関数が作成されます。
もし新しいメソッドのラムダリストがジェネリック関数のラムダリストに合致していない場合は、 エラーが発せられます。 もしジェネリック関数オプションを指定できないメソッド定義オペレーターが 新しいジェネリック関数を作成する場合は、 ジェネリック関数のラムダリストは、メソッドのものと合致するように、 メソッド定義フォームから生成されるメソッドのラムダリストから導出されます。 合致の議論については、7.6.4 ジェネリック関数の全てのメソッドのラムダリストの合意を参照。
各メソッドは特定されたラムダリストを持っており、それはメソッドが適用されたときに決定します。
特定されたラムダリストは、通常のラムダリストに似ていますが、
要求パラメータの名前の代わりに特定パラメーターとなるのが違っています。
特定パラメーターは(変数名 特定パラメーター名)
のリストであり、
特定パラメーター名は次のうちの1つを取ります。
シンボル:特定パラメーターはシンボルによるクラス名です。
クラス:特定パラメーターはクラス自身です。
(eql form)
:特定パラメーターは型特定子(eql object)
を満たし、object
はform
を評価した結果となります。form
フォームはメソッド定義フォームが評価された中でのレキシカル環境内によって評価されます。 注意として、form
はメソッドが定義されたときに、ただ一度だけ評価されます。 ジェネリック関数が呼び出されるたびに評価されるのではありません。
特定パラメーター名は、ユーザーレベルのインターフェースである
defmethod
のようなマクロで使用されることを意図しており、
特定パラメーターは関数のインターフェース上で使われます。
要求パラメーターのみを特定化することができ、
各要求パラメーターは特定パラメーターが存在しなければなりません。
表記を単純にするために、もしメソッド定義フォームの特定化されたラムダリスト内において、
要求パラメーターが単純に変数名だけであった場合は、
その要求パラメーターはデフォルトのクラスt
が指定されます。
ジェネリック関数に引数の集合が与えられたとき、 適用するメソッドは、特定パラメーターが対応する引数によって満たされるジェネリック関数のメソッドです。 次の定義は、メソッドが適用可能かどうか、また引数が特定パラメーターを満たすかどうかとは、 どういう意味であるのかを示します。
<A1, ..., An>
がジェネリック関数の要求パラメーターの順番であるとします。
<P1, ..., Pn>
がメソッドM
における要求パラメーターに対応する特定パラメーターの順番であるとします。
各Ai
の型が、型特定子Pi
によって特定されるとき、メソッドM
は適用可能です。
全ての有効な特定パラメータは有効な型指定子でもあるため、
関数typep
は、メソッドの選択において、
引数が特定パラメーターを満たすかどうかを決定するために使用できます。
全ての特定パラメーターがクラスt
のメソッドは、デフォルトメソッドと呼ばれます。
これは常に適用可能ですが、他のもっと特定的なメソッドによってシャドウされるかもしれません。
メソッドは限定子を持てます。 これはmethod-combinationがメソッドを区別するための方法としての手順を与えます。 1つか複数の限定子を持っているメソッドは、限定されたメソッドと呼ばれます。 限定子を持っていないメソッドは、限定されていないメソッドと呼ばれます。 限定子はリスト以外のオブジェクトです。 標準のmethod-combinationによって定義された限定子の型はシンボルです。
この定義の中で、「プライマリメソッド」と「補助メソッド」という語は、
これらを使用するmethod-combinationタイプにおいて、メソッドを区分けするために使用されます。
method-combinationのstandard
では、プライマリメソッドは限定されていないメソッドであり、
補助メソッドは単一の限定子である:around
, :before
, :after
のうちの1つを指定したメソッドです。
これらのメソッドは、順にaround
メソッド、before
メソッド、after
メソッドと呼びます。
method-combinationタイプがdefine-method-combination
の
短いフォームを使用して定義されたとき、
プライマリメソッドはmethod-combinationタイプの名前を限定子に与えたメソッドになります。
そして補助メソッドは:around
の限定子です。
このように「プライマリメソッド」と「補助メソッド」という語は、
method-combinationタイプにおける、ただの相対的な定義となります。
7.6.3 特定パラメーターと限定子の合致
2つのメソッドが特定パラメーターと限定子それぞれにおいて 互いに合致したと言えるのは、次に示す状況が当てはまる場合です。
両方のメソッドが同じ数の要求パラメーターを持っているとき。 例えば、2つのメソッドの特定パラメーターが、
P1,1 ... P1,n
とP2,1 ... P2,n
であったとき。1≦i≦n
の各P1,i
がP2,i
に一致したとき。 特定パラメーターP1,i
がP2,i
に一致したとは、P1,i
とP2,i
が同じクラスであるか、 あるいはP1,i=(eql object1)
,P2,i=(eql object2)
,(eql object1 object2)
のとき。 それ以外では、P1,i
とP2,i
は一致していません。2つの限定子のリストが、
equal
で等しいとき。
7.6.4 ジェネリック関数の全てのメソッドのラムダリストの合意
下記に示すこれらの定義は、ラムダリストの集合の合意を定義します。 ラムダリストには、指定したジェネリック関数の各メソッドのラムダリストと、 ジェネリック関数自身で定義されたラムダリストを含みます。
各ラムダリストは、同じ数の要求パラメーターを持つ必要があります。
各ラムダリストは、同じ数の
&optional
パラメーターを持つ必要があります。 各メソッドは、&optional
パラメーターに独自のデフォルト値を提供することができます。もしどれかのラムダリストが
&rest
か&key
を持つなら、 各ラムダリストはそのうちの1つか両方を指定する必要があります。もしジェネリック関数のラムダリストが
&key
を持つなら、 各メソッドは&key
の後の全てのキーワードの名前を受け付けるようにする必要があります。 受け付ける方法は、全ての名前を明に指定する方法、 あるいは&allow-other-keys
を指定する方法がありますが、&key
の指定ではなく&rest
を指定する方法でも問題ありません。 各メソッドは、独自のキーワード引数を追加で受け付けることができます。 キーワードの名前の有効性のチェックはジェネリック関数が行い、各メソッドでは行いません。 メソッドが実行されたときには、キーワード引数に名前が:allow-other-keys
、 値がtrue
であるペアが与えられたように呼び出されますが、 そのような引数のペアは渡されません。&allow-other-keys
の使用は、ラムダリスト間で一貫している必要はありません。 もし&allow-other-keys
がジェネリック関数か適用メソッドのラムダリストに指定されている場合、 ジェネリック関数が呼び出されるときには、どんなキーワード引数も受け付けるでしょう。&aux
の使用は、メソッド間で一貫している必要はありません。
もしジェネリック関数のオプションを指定できないメソッド宣言オペレーターが
ジェネリック関数を作成した場合、
さらにメソッドのラムダリストにキーワードパラメーターが指定されている場合は、
ジェネリック関数のラムダリストには&key
が指定されます(しかしキーワード引数は指定されない)。
7.6.5 ジェネリック関数とメソッドのキーワード引数
ジェネリック関数かそのメソッドがラムダリストに&key
を指定しているとき、
ジェネリック関数によって受け取れるキーワード引数の集合の定義は、
適用可能なメソッドによって変化します。
ある呼び出しにおいてジェネリック関数が受け取れるキーワード引数の集合とは、
適用可能な全てのメソッドによって受け取ることができるキーワード引数と、
ジェネリック関数に&key
があるならば&key
以降に示されるキーワード引数の集合です。
&key
の指定が無く、&rest
の指定があるメソッドは、
受け付けるキーワード引数の集合には影響しません。
もし適用可能なメソッドのどれかのラムダリストか、
あるいはジェネリック関数の宣言によるラムダリストが&allow-other-keys
を含んでいる場合は、
そのジェネリック関数は全てのキーワード引数を受け取ります。
ラムダリスト一致の規則は、
各メソッドが次のような全てのキーワード引数を受け付けるようにすることを要求します。
それは、ジェネリック関数の定義において&key
の後に指定したものを明に受け取れるようにするか、
&allow-other-keys
を指定するか、あるいは&key
が無い場合に&rest
を設定するかになります。
各メソッドは、ジェネリック関数の定義にあるキーワード引数に加えて、
独自のキーワード引数を追加で受け取るようにすることができます。
もしジェネリック関数は、渡されたキーワード引数がどの適用メソッドにも受け付けられなかった場合は、 エラーを発する必要があります。3.5 関数呼び出しのエラーチェックを参照。
7.6.5.1 ジェネリック関数とメソッドのキーワード引数の例
例えば、下記の2つのwidth
メソッドが定義されていることを考えます。
(defmethod width ((c character-class) &key font) ...) (defmethod width ((p picture-class) &key pixel-size) ...)
その他のwidth
以外のメソッドとジェネリック関数は存在しないと仮定します。
下記のフォームを評価したときには、
キーワード引数:pixel-size
が適用可能なメソッドで受け付けられないため、
エラーが発せられます。
(width (make-instance 'character-class :char #\Q) :font 'baskerville :pixel-size 10)
下記のフォームの評価は、エラーが発せられます。
(width (make-instance 'picture-class :glyph (glyph #\Q)) :font 'baskerville :pixel-size 10)
下記のフォームの評価は、もしcharacter-picture-class
という名前のクラスが、
picture-class
とcharacter-class
両方のサブクラスであった場合には、
エラーにはならないでしょう。
(width (make-instance 'character-picture-class :char #\Q) :font 'baskerville :pixel-size 10)
7.6.6 メソッドの選択とコンビネーション
ジェネリック関数が特定の引数とともに呼び出されたとき、 実行するコードを決定しなければなりません。 このコードは、これらの引数に対する有効なメソッドと呼ばれます。 有効なメソッドは、ジェネリック関数内の適用可能なメソッドを結びつけたものであり、 このメソッドのいくつかか、あるいは全てのメソッドが呼び出されます。
もしジェネリック関数が呼び出されたときに、適用可能なメソッドが存在しなかった場合は、
ジェネリック関数no-applicable-method
が呼び出されます。
その呼び出した結果の返却値は、最初のジェネリック関数が呼び出されたものの返却値として使用されます。
no-applicable-method
の呼び出しは、キーワード引数が受付可能かどうか先行してチェックされます。
7.6.5 ジェネリック関数とメソッドのキーワード引数を参照。
もし有効なメソッドが決定されたら、 ジェネリック関数に渡されたものと同じ引数とともに呼び出されます。 どのような返却値であれ、それはジェネリック関数が返却した値として返却されます。
7.6.6.1 有効なメソッドの決定
有効なメソッドは、下記の3ステップによって決定されます。
適用可能なメソッドの選択します。
優先順位によって適用可能なメソッドをソートし、最も特定的なメソッドを最初に選択します。
ソートされた適用可能なメソッドをmethod-combinationに渡して実行し、有効なメソッドを生成します。
7.6.6.1.1 適用可能なメソッドの選択
この手順は、7.6.2 メソッドの紹介で定義されています。
7.6.6.1.2 優先順位による適用可能なメソッドのソート
2つのメソッドの優先順位を比べるために、特定パラメーターが順番に調べられます。
デフォルトの調査順は左から右ですが、defgeneric
か、あるいは別のオペレーターによる
ジェネリック関数の:argument-precedence-order
オプションによって、逆順に指定されます。
各メソッドの対応する特定パラメーターが比較されます。 特定パラメーターのペアが一致していたら、次のペアが比較されます。 もし対応する全ての特定パラメーターが一致していたのであれば、 2つのメソッドは違った限定子を持っている必要があります。 このケースの場合、メソッドはどちらの順番でも選択できます。 一致についての詳細は、7.6.3 特定パラメーターと限定子の合致を参照。
もしいくつかの特定パラメーターが一致していなかった場合、 最初に一致しなかった特定パラメーターのペアが優先順位を決定します。 もしどちらの特定パラメーターもクラスであったとき、 その2つのメソッドの対応する特定パラメーターを見て、 クラス優先リストの中に早く現われた方がより特定的なメソッドとなります。 適用可能なメソッドの集合から選択する方法を行っているため、 特定パラメーターは引数のクラスのクラス優先リストに存在することが保証されます。
もし対応する特定パラメーターのペアのうち、ちょうど1つが(eql object)
であったときは、
その特定パラメーターを持つメソッドが、他のメソッドより優先します。
もし両方の特定パラメーターがeql
形式であったときは、
特定は一致するとしなければなりません
(そうでなければ2つのメソッドはこの引数において両方とも適用できなかったでしょう)。
適用可能なメソッドリストの結果は、最も特定的なメソッドが最初であり、 最も特定的ではないメソッドが最後になります。
7.6.6.1.3 ソートされた適用可能なメソッドのMethod-Combination実行
単純な場合として、method-combinationはstandard
が使われており、
全ての適用可能なメソッドはプライマリメソッドであるとします。
この場合は、有効メソッドは最も特定的なメソッドとなります。
メソッドは、次に特定的なメソッドを関数call-next-method
の使用にて呼び出すことができます。
call-next-method
によって呼び出されるメソッドは、次のメソッドと言います。
関数next-method-p
は、次のメソッドが存在するかどうかをテストします。
もしcall-next-method
が呼ばれたものの、次の特定的なメソッドが存在しなかった場合は、
ジェネリック関数no-next-method
が呼び出されます。
一般的に、有効なメソッドは、適用可能なメソッドを組み合わせた結果のいくつかとなります。
これは次に記載されたような目的によりフォームとして定義されます。
適用可能なメソッドは、いくつかが呼ばれるか、あるいは全部が呼ばれるかを定義します。
また、返却値は1つか複数が返却されるように定義します。
その返却値はジェネリック関数として返却されるものです。
付加的にはいくつかのメソッドがcall-next-method
を用いてアクセス可能になるように定義します。
有効なメソッドにおける各メソッドの役割は、 メソッドの限定子と特定子によって決定されます。 限定子はメソッドに印をつけるものであり、 限定子の意味は手続きにおいて印を用いることで決定されます。 もし適用可能なメソッドが認識できない限定子を持っていた場合はエラーを発し、 有効なメソッドの中にこのメソッドが存在しないものとします。
method-combinationのstandard
が限定されたメソッドと一緒に使われたときは、
有効なメソッドは7.6.6.2 Standard Method-Combinationに記載されたものとして生成されます。
他のタイプのmethod-combinationは、defgeneric
かあるいは別のオペレーターで
ジェネリック関数のオプション:method-combination
を使うことで使用できます。
この方法により、手順をカスタマイズできます。
新しいタイプのmethod-combinationは、
define-method-combination
マクロを使うことによって定義することができます。
7.6.6.2 Standard Method-Combination
method-combinationのstandard
は、standard-generic-function
クラスによって提供されます。
これはmethod-combinationのタイプが指定されなかった場合か、
あるいは組み込みのmethod-combinationタイプであるstandard
が指定された場合に使われます。
プライマリメソッドは有効なメソッドのメインとなる動作として定義されます。 補助メソッドは3つあるうちの1つの方法を用いて動作を変更します。 プライマリメソッドは限定子を持ちません。
補助メソッドは、限定子:before
, :after
, そして:around
のメソッドです。
method-combinationのstandard
は、メソッドに対して2つ以上の限定子を許容しません。
もしメソッド定義で複数の限定子をもつメソッドを定義した場合は、エラーが発せられます。
before
メソッドは、ただひとつの限定子として:before
キーワードを持ちます。before
メソッドは、プライマリメソッドの前に実行されるコードを定義します。after
メソッドは、ただひとつの限定子として:after
キーワードを持ちます。after
メソッドは、プライマリメソッドの後に実行されるコードを定義します。around
メソッドは、ただひとつの限定子として:around
キーワードを持ちます。around
メソッドは、他の適用可能なメソッドの代わりとして実行されますが、 いくつかのシャドウされたメソッドを(call-next-method
経由で)呼び出すコードを、 明に含むことができます。
method-combinationのstandard
の意味を次に示します。
もし
around
メソッドが存在する場合は、最も特定的なaround
メソッドが呼ばれます。 これはジェネリック関数に対して1つか複数の返却値を提供します。around
メソッドのコード内では、次のメソッドを呼ぶためのcall-next-method
が使用できます。 次のメソッドから戻ったとき、around
メソッドは返却された値に基づいて、 さらにコードを実行することができます。 もしcall-next-method
を使用したときに呼び出せる適用可能なメソッドが存在しなかった場合は、 ジェネリック関数no-next-method
が呼び出されます。 関数next-method-p
は、次のメソッドが存在するかどうかを決定するために使われます。もし
around
メソッドがcall-next-method
を実行したとき、 次の特定的なaround
メソッドが適用可能であれば呼び出されます。 もしaround
メソッドが存在しないか、 あるいは最も特定的ではないaround
メソッドによってcall-next-method
が呼び出された場合は、 次に示すものとして他のメソッドが呼び出されます。全ての
before
メソッドがmost-specific-first
の順番で呼ばれます。 これらの返却値は無視されます。 もしbefore
メソッド内でcall-next-method
が使用された場合は、エラーが発せられます。最も特定的なプライマリメソッドが呼び出されます。 プライマリメソッドのコード内では、 次の特定的なプライマリメソッドを呼び出すための
call-next-method
が使用できます。 メソッドから戻ったとき、以前のプライマリメソッドは返却された値に基づいて、 さらにコードを実行することができます。 もしcall-next-method
を使用したときに、呼び出せる適用可能なメソッドが存在しなかった場合は、 ジェネリック関数no-next-method
が呼び出されます。 関数next-method-p
は、次のメソッドが存在するかどうかを決定するために使われます。 もしcall-next-method
が使われなかった場合は、最も特定的なプライマリメソッドだけが呼び出されます。全ての
after
メソッドがmost-specific-last
の順番で呼ばれます。 これらの返却値は無視されます。 もしafter
メソッド内でcall-next-method
が使用された場合は、エラーが発せられます。
もし
around
メソッドが呼び出されなかった場合は、 最も特定的なプライマリメソッドが、 1つか複数の値をジェネリック関数の返却値として返却します。 最も特定的ではないaround
メソッドからcall-next-method
の呼び出しによって返却される1つか複数の返却値は、 最も特定的なプライマリメソッドの返却値となります。
method-combinationのstandard
では、適用可能なメソッドが存在しても、
適用可能なプライマリメソッドが存在しなかった場合はエラーが発せられます。
before
メソッドはmost-specific-first
順にて実行され、
after
メソッドはleast-specific-first
順に実行されます。
この設計の違いの根拠を、例として次のように示します。
クラスC1
がスーパークラスであるC2
の動作を、
before
メソッドとafter
メソッドに追加することで変更することを考えます。
クラスC2
の振る舞いがC2
のメソッドとして直接定義するか、
あるいはスーパクラスを継承によるものかに関わらず、
クラスC1
のインスタンスによって呼び出されるメソッドの相対的な順番には影響しません。
クラスC1
のbefore
メソッドは、クラスC2
の全てのメソッドの前に実行されます。
クラスC1
のafter
メソッドは、クラスC2
の全てのメソッドの後に実行されます。
対称的に、全てのaround
メソッドが実行されるのは、他のメソッドが実行される前です。
このように最も遠いaround
メソッドは、最も特定的なプライマリメソッドの前に実行されます。
もしプライマリメソッドのみが宣言されており、
さらにcall-next-method
が使用されなかった場合は、
最も特定的なメソッドのみが実行されます。
つまり最も特定的なメソッドが他の一般的なメソッドをシャドウしたということです。
7.6.6.3 Method-Combinationの宣言
マクロdefine-method-combination
は、method-combinationの新しいフォームを定義します。
これは、有効なメソッドの生成をカスタマイズする仕組みを提供します。
標準の有効なメソッドの生成手順は、7.6.6.1 有効なメソッドの決定に記載されています。
define-method-combination
には、2つのフォームが存在します。
短いフォームは単純な宣言方法であり、長いフォームはもっと強力で冗長です。
長いフォームはdefmacro
と似ています。
コード本体は式であり、Lispフォームを計算します。
これはmethod-combination内の構造を任意に制御する仕組みと、
メソッドの限定子を任意に扱う仕組みを提供します。
7.6.6.4 組み込みのMethod-Combination
オブジェクトシステムは、組み込みのmethod-combinationタイプをいくつか提供しています。
これらのmethod-combinationタイプのうちの1つを、ジェネリック関数で使うことができます。
指定する方法は、method-combinationタイプの名前を、
defgeneric
の:method-combination
オプションに引数として与えるか、
その他のオペレーターでジェネリック関数のオプション:method-combination
を指定することです。
組み込みのmethod-combinationタイプの名前を、次の表に示します。
+ and append list max min nconc or progn standard 表7-2 組み込みのMethod-Combinationタイプ
組み込みのmethod-combinationタイプであるstandard
の意味は、
7.6.6.2 Standard Method-Combinationに記載しました。
他の組み込みmethod-combinationタイプは、
シンプルな組み込みmethod-combinationタイプと呼ばれています。
シンプルなmethod-combinationタイプは、
短い形式のdefine-method-combination
によって定義されたかのように動作します。
メソッドは次の2つの役割を認識します。
around
メソッドは、ただひとつの限定子として:around
キーワードを持ちます。around
メソッドの意味はmethod-combinationのstandard
と同じです。around
メソッド内ではcall-next-method
とnext-method-p
の関数の使用が提供されます。プライマリメソッドは、ただひとつの限定子としてmethod-combinationの名前を持ちます。 例えば、組み込みのmethod-combinationタイプの
and
は、 ただひとつの限定子であるand
を指定したメソッドをプライマリメソッドとして認識します。call-next-method
とnext-method
の関数はプライマリメソッドでは提供されません。
シンプルな組み込みmethod-combinationタイプの意味を下記に示します。
もし
around
メソッドが存在するならば、最も特定的なaround
メソッドが呼ばれます。 これはジェネリック関数へ1つか複数の返却値を提供します。around
メソッドのコード内では、次のメソッドを呼ぶためのcall-next-method
が使用できます。 もしcall-next-method
を使用したときに、呼び出せる適用可能なメソッドが存在しなかった場合は、 ジェネリック関数no-next-method
が呼び出されます。 関数next-method-p
は、次のメソッドが存在するかどうかを決定するために使われます。 次のメソッドから戻ったとき、around
メソッドは返却された値に基づいて、 さらにコードを実行することができます。もし
around
メソッドがcall-next-method
を実行したとき、 次の特定的なaround
メソッドが適用可能であれば呼び出されます。 もしaround
メソッドが存在しないか、 あるいは最も遠いaround
メソッドによってcall-next-method
が呼び出されたときは、 組み込みmethod-combinationタイプの名前と適用可能なプライマリメソッドのリストから、 評価されるとジェネリック関数の返却値を生成する様なLispフォームが導出されます。 例えば、method-combinationタイプの名前がoperator
のとき、 ジェネリック関数が次のフォームによって呼び出されることを考えます。
(generic-function a1...an)
ここでM1, ... Mk
はこの順に適用可能なプライマリリストであるとします。
そのとき、Lispフォームは次のように導出されます。
(operator <M1 a1...an> ... <Mk a1...an>)
もし式<Mi a1...an>
が評価されたとき、メソッドMi
は引数a1...an
を適用します。
例えばoperator
がor
のとき、式<Mi a1...an>
は、
ただ<Mj a1...an>
, 1≦j<i
がnil
を返却したときのみに評価されます。
プライマリメソッドのデフォルトの順番は:most-specific-first
です。
しかし、:method-combination
オプションの2番目の引数に
:most-specific-last
を指定したときは逆順にすることができます。
シンプルな組み込みmethod-combinationタイプは、
メソッドに対して正確に1つの限定子を要求します。
もし限定子が存在しない適用可能なメソッドか、
あるいはmethod-combinationタイプが認識しない限定子を指定したときはエラーが発せられます。
もしaround
メソッドが存在するものの、プライマリメソッドが存在しない場合は、エラーが発せられます。
7.6.7 メソッドの継承
サブクラスは、クラスの全てのインスタンスのどんな適用可能なメソッドも継承します。 また、クラスのどんなサブクラス全てのインスタンスにも適用可能です。
メソッドの継承は、メソッド定義オペレーターによって作成されたメソッドであっても、 同じ方法によって行われます。
メソッドの継承の詳細は、7.6.6 メソッドの選択とコンビネーションに記載されています。