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型の名前であり、 残りの引数はその型へのオプションです。