% Macro DEFINE-METHOD-COMBINATION
Macro DEFINE-METHOD-COMBINATION
define-method-combination
name [[short-form-option]]
=> name
define-method-combination
name lambda-list
(method-group-specifier*)
[(:arguments . args-lambda-list)]
[(:generic-function generic-function-symbol)]
[[declaration* |
documentation]]
form*
=> name
short-form-option::= :documentation documentation |
:identity-with-one-argument identity-with-one-argument |
:operator operator
method-group-specifier::= (name {qualifier-pattern+ | predicate} [[long-form-option]])
long-form-option::= :description description |
:order order |
:required required-p
args-lambda-list - define-method-combination引数のラムダリスト
declaration - 宣言の式、評価はされません
description - format-control
documentation - 文字列、評価はされません
form - 暗黙のprogn。
有効なメソッドをどのようにして結合させるかを計算し、
フォームとして返却しなければなりません。
generic-function-symbol - シンボル
identity-with-one-argument - generalized-boolean
lambda-list - 通常のラムダリスト
name - シンボル。
通常、キーワードではなく、nil
でもないシンボルが使われます。
operator - オペレーター。
nameとoperatorは、よく同じシンボルが使われます。
これがデフォルトですが、必須ではありません。
order - :most-specific-first
か:most-specific-last
。評価されます。
predicate - シンボルであり、
それはひとつの引数を取りgeneralized-booleanを返却する関数名。
qualifier-pattern - リストか、シンボル*
required-p - generalized-boolean
マクロdefine-method-combination
は、
新しいmethod-combinationの型を定義するときに使います。
define-method-combination
には2つのフォームがあります。
短いフォームは単純な機能であり、
もっとも一般的に必要があると予想されるケースに対応しています。
長いフォームはより強力で、より冗長です。
これは、defmacro
のように、
通常はボディ部がバッククォートを使った式であり、
フォームを計算します。
したがって、任意の制御構造を実装することができます。
長いフォームは、メソッド修飾子を任意に処理することもできます。
define-method-combination
の短いフォームの構文は、
二番目のサブフォームがnil
ではないシンボルか、
あるいは現れていないときに認識されます。
短いフォームを使うと、
nameが(operator method-call method-call ...)
という
Lispのフォームが生成されるmethod-combinationの型として定義されます。
operatorは関数か、マクロか、
特殊オペレーターの名前のシンボルです。
operatorはキーワードオプションによって指定することができ、
デフォルト値はnameです。
:documentation
オプションは、
method-combination型のドキュメントに使われます。
定義は下記にある長いフォームを参照。:identity-with-one-argument
オプションは、
値がtrueのときに最適化を有効にします。
(デフォルトはfalseです。)
もし適用可能なメソッドが正確にひとつであり、
それがプライマリメソッドのとき、
そのメソッドは有効なメソッドとして機能し、
operatorを呼び出しません。
この最適化は、新たに有効なメソッドを作成することがなくなり、
また関数呼び出しのオーバーヘッドを抑えます。
このオプションは
progn
, and
, +
, max
のようなオペレーターで
で使用することを想定しています。:operator
オプションは、オペレーターの名前を指定します。
operator引数は、関数か、マクロか、
特殊オペレーターの名前のシンボルです。:around
が与えられたメソッドは、
standard
method-combinationのaroundメソッドと
同じふるまいをする補助メソッドです。
関数call-next-method
は、aroundメソッド内でのみ使用できます。
つまり、define-method-combination
マクロの
短いフォームによって定義された
プライマリメソッド内では使用することができません。
:most-specific-first
です。
値:most-specific-last
は、
補助メソッドの順番に影響することなく、
プライマリメソッドの順番を逆順にさせます。
define-method-combination
の長いフォームの構文は、
二番目のサブフォームがリストのときに認識されます。
defgeneric
の:method-combination
オプションの
型method-combinationの名前の後に提供されている
どのような引数も受け取ります。
car
部は、変数名のシンボルです。
define-method-combination
のボディ部のフォームを実行しているとき、
この変数はメソッドグループ内のメソッドのリストに束縛されます。
このリスト中のメソッドは、:order
オプションによって順序付けられます。
*
でなければなりません。
もしメソッドの修飾子のリストと
qualifier-patternがequal
で一致する場合
(ただしシンボル*
はどんなqualifier-patternにもマッチします)、
メソッドはqualifier-patternにマッチすると言えます。
したがって、qualifier-patternは、次に示すうちのひとつに当てはまります。
空のリストの場合は、修飾子がないメソッドにマッチします。
シンボル*
は、すべてのメソッドにマッチします。
通常のリストのときは、
メソッドにはリストの長さと同じ数の修飾子があり、
各修飾子が対応するリストの要素にマッチします。
もしドットリストのときは、シンボル*
で終端します
(*
は、続くどんな数の修飾子にもマッチします)。
invalid-method-error
が呼び出されます。
*
でもないので、
追加のqualifier-patternと区別することができます。
キーワードオプションは次の通りです。
:description
オプションは、
メソッドグループ内のメソッドの役割の説明を指定するときに使われます。
プログラミング環境のツールは、
簡潔な説明を出力するときは、
(apply #'format stream format-control (method-qualifiers method))
を
実行することを期待しています。
ほとんどの場合、format-controlはformat
指示子を含みませんが、
一般性のために利用できます。:order
オプションは、メソッドの順序を指定します。
order引数は、:most-specific-first
か:most-specific-last
に
評価されるフォームです。
もしそのほかの値に評価された場合はエラーが発生します。
:order
が指定されなかったときは、デフォルトは:most-specific-first
です。:require
オプションは、
メソッドグループに少なくともひとつのメソッドが必要かどうかを指定します。
もし値がtrueであり、かつメソッドグループが空のとき
(これは、適用可能なメソッドが、qualifier-patternにマッチしないか
あるいはpredicateを満たさなかったとき)
エラーが発生します。
:require
が指定されないときは、デフォルトはnil
です。method-qualifiers
とinvalid-method-error
の関数を使用することで、
さらなるメソッドのフィルタリングを行うことができます。
メソッドグループ指定子内で名前付けられた変数に
setq
を使うこともできますし、
追加の変数に束縛することもできます。
また、メソッドグループ指定子の仕組みをバイパスして、
ボディ部のフォーム内ですべてを行うこともできます。
これは、qualifier-patterが*
のみである
単体のメソッドグループを記述することで実現できます。
変数にはすべての適用可能なメソッドのリストが
most-specific-first
順に束縛されます。
null
のレキシカルな環境から
加えてローカルマクロcall-method
の定義と、
COMMON-LISP-USER
パッケージからアクセスできないシンボル名の束縛で
拡張された環境下で評価されます。
メソッドグループ指定子によって生成されたリストのうちの
ひとつのメソッドオブジェクトと、次のメソッドのリストが与えられると、
call-method
は、call-next-method
が次のメソッドとして利用可能な
メソッドを呼び出します。
nil
か
あるいは指定されていないようなcall-method
マクロの呼び出しで
全体が構成されているような場合において有効です。
各define-method-combination
のボディ部は
この最適が必要な場合は、
progn
, and
, multiple-value-prog1
などの
冗長な呼び出しを削除する責任を負います。
(:arguments . lambda-list)
は、
宣言かドキュメント文字の前に記述することができます。
このフォームは、method-combinationが
メソッドを結合するための一部としてある特定の動作を行い、
続けてジェネリック関数の引数にアクセスする
必要が生じた場合において有効です。
lambda-listによって定義された各パラメーターの変数は、
有効なメソッドの中に挿入されたフォーム内で束縛されます。
このformが有効なメソッドの実行中に評価されるとき、
それらの値はジェネリック関数に対応する引数になります。
そのようなフォームをsetf
のplace
として使用した時の結果は未定です。
引数の対応は、:arguments
のlambda-listと、
ジェネリック関数のlambda-listを、
それぞれ次の三つのセクションに分けて計算します。
その三つは、要求パラメーター、オプションパラメーター、
そして、キーワードとrest
パラメーターです。
ある特定の呼び出し時にジェネリック関数に指定された引数もまた、
三つのセクションに分割されます。
要求された引数のセクションは、
ジェネリック関数が保有する要求された引数と同じ数の引数を含み、
オプション引数のセクションは
ジェネリック関数が保有するオプション引数と同じ数の引数を含み、
そしてキーワードとrest
引数のセクションは残りの引数を含みます。
:arguments
のlambda-listにある
要求された引数とオプション引数の
セクション内にある各パラメーターは、
対応する引数のセクションと
同じ位置にある引数にアクセスします。
もしarguments
のlambda-listのセクションの方が短いときは、
余分な引数は無視されます。
もしarguments
のlambda-listのセクションの方が長いときは、
超過した要求パラメーターはnil
が束縛され、
超過したオプションパラメーターはそれぞれのinitform
に束縛されます。
:arguments
のlambda-listのキーワードとrest
パラメーターは、
引数のキーワードとrest
のセクションにアクセスされます。
もし:arguments
のlambda-listに&key
が含まれているときは、
&allow-other-keys
が含まれているかのようにふるまいます。
:arguments
のlambda-listの最初に
&whole var
配置することができます。
これは、ジェネリック関数に指定されたすべての引数のリストを、
評価されるformに対してvarに束縛します。
これはキーワードやrest
引数だけでなく、
すべての引数にアクセスするので、&rest
とは異なります。
method-combination-error
かinvalid-method-error
で
報告するべきです。
これらの関数は、エラーメッセージに必要な文脈的情報を追加し、
そして適切なエラーを通知します。
method-combination
で)
method-combinationオブジェクトに割り当てられます。
define-method-combination
の修飾子の
パターンマッチの部分で発生します。もしdefine-method-combination
フォームがトップレベルフォームに現れたとき、
コンパイラーはmethod-combinationの名前が、
続くdefgeneric
フォームのmethod-combination名で
有効であると認識させなければなりません。
しかし、method-combinationが実行されるのは、
define-method-combination
フォームの実行時より早くはないので、
ジェネリック関数がmethod-combinationを使うときまで
可能な限り遅らせて実行されます。
define-method-combination
の長いフォームの例のほとんどは、
メソッド結合の機能の宣言の一部として、
提供されている関係関数の使用も説明をしています。
;;; define-method-combinationの短いフォームの例
(define-method-combination and :identity-with-one-argument t)
(defmethod func and ((x class1) y) ...)
;;; このサンプルと同等の長いフォームの例:
(define-method-combination and
(&optional (order :most-specific-first))
((around (:around))
(primary (and) :order order :required t))
(let ((form (if (rest primary)
`(and ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;;; define-method-combinationの長いフォームの例
;The default method-combination technique
(define-method-combination standard ()
((around (:around))
(before (:before))
(primary () :required t)
(after (:after)))
(flet ((call-methods (methods)
(mapcar #'(lambda (method)
`(call-method ,method))
methods)))
(let ((form (if (or before after (rest primary))
`(multiple-value-prog1
(progn ,@(call-methods before)
(call-method ,(first primary)
,(rest primary)))
,@(call-methods (reverse after)))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form))))
;どれかがnon-nilを返却するまでメソッドを実行する単純な例
(define-method-combination or ()
((methods (or)))
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
methods)))
;先行のより完成されたバージョン
(define-method-combination or
(&optional (order ':most-specific-first))
((around (:around))
(primary (or)))
;; order引数の処理
(case order
(:most-specific-first)
(:most-specific-last (setq primary (reverse primary)))
(otherwise (method-combination-error "~S is an invalid order.~@
:most-specific-first and :most-specific-last are the possible values."
order)))
;; プライマリメソッドは必要
(unless primary
(method-combination-error "A primary method is required."))
;; プライマリメソッドを呼び出すフォームの構築
(let ((form (if (rest primary)
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
;; aroundメソッドを囲むフォーム
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;オプション:orderと:requireを使った同じ例
(define-method-combination or
(&optional (order ':most-specific-first))
((around (:around))
(primary (or) :order order :required t))
(let ((form (if (rest primary)
`(or ,@(mapcar #'(lambda (method)
`(call-method ,method))
primary))
`(call-method ,(first primary)))))
(if around
`(call-method ,(first around)
(,@(rest around)
(make-method ,form)))
form)))
;この短いフォームの呼出しは先行と同一のふるまいをする
(define-method-combination or :identity-with-one-argument t)
;qualifiersの正の整数によるメソッドの順序付け
;この小さな例では:aroundメソッドは許されない
(define-method-combination example-method-combination ()
((methods positive-integer-qualifier-p))
`(progn ,@(mapcar #'(lambda (method)
`(call-method ,method))
(stable-sort methods #'<
:key #'(lambda (method)
(first (method-qualifiers method)))))))
(defun positive-integer-qualifier-p (method-qualifiers)
(and (= (length method-qualifiers) 1)
(typep (first method-qualifiers) '(integer 0 *))))
;;; 引数:argumentsの使用例
(define-method-combination progn-with-lock ()
((methods ()))
(:arguments object)
`(unwind-protect
(progn (lock (object-lock ,object))
,@(mapcar #'(lambda (method)
`(call-method ,method))
methods))
(unlock (object-lock ,object))))
なし。
コンパイラーはコンパイル時の副次的な処理を行う必要はありません。
短いフォームで定義されたmethod-combinationの型は、
メソッドに対して正確にひとつの修飾子を要求します。
もし適用可能なメソッドに、修飾子がなかったり、
method-combinationの型がサポートされていない
修飾子であったときは、
型error
のエラーが発生します。
少なくともひとつのプライマリメソッドが適用可能でないと
型error
のエラーが発生します。
もしどのメソッドグループにも属さない適用可能なメソッドがある場合は、
システムは、型error
のエラーを通知し、
そのメソッドが使用中のmethod-combinationの種類に対して無効であることを示します。
もし:require
オプションの値がtrueであり、かつメソッドグループが空のとき
(これは、適用可能なメソッドが、qualifier-patternにマッチしないか
あるいはpredicateを満たさなかったとき)
型error
のエラーが発生します。
もしorder引数が評価された値が、
:most-specific-first
か:most-specific-last
の
どちらでもなかった場合は型error
のエラーが発生します。
call-method
,
call-next-method
,
documentation
,
method-qualifiers
,
method-combination-error
,
invalid-method-error
,
defgeneric
,
7.6.6. メソッドの選択とコンビネーション,
7.6.6.4. 組み込みのMethod-Combination,
3.4.11. ドキュメント文字と宣言の文脈的な作用
defgeneric
の:method-combination
オプションは、
ジェネリック関数が特定のmethod-combination型を使用するように
指定するときに使われます。
:method-combination
オプションの最初の引数はmethod-combination型の名前であり、
残りの引数はその型へのオプションです。