Next: 関数セルの内容へのアクセス, Previous: 無名関数, Up: 関数 [Contents][Index]
defun
を使用して定義された関数は、その引数の型と期待する値に関して、ハードコードされた一連の仮定をもちます。たとえば数字か数字のリストを引数値として処理するようにデザインされた関数は、ベクターや文字列のような他の型の値で呼び出されると失敗したりエラーをシグナルするでしょう。これはその関数実装が、デザイン時に想定した以外の型に対応しないために発生します。
対照的に多相型関数(polymorphic functions)を使用したオブジェクト指向プログラムでは、同一の名前をもつ一連の特化した関数のそれぞれが、特定の引数型セットにたいして記述されます。どの関数が実際に呼び出されるかは、実際の引数の型にもとづいて実行時に決定されます。
Emacsはポリモーフィズム(polymorphism)にたいするサポートを提供します。他のLisp環境、特にCommon LispとCommon Lispオブジェクトシステム(CLOS)と同じように、このサポートはジェネリック関数(generic functions)を基礎としています。Emacsのジェネリック関数は同一名の使用を含むCLOSに密接にしたがっているので、CLOSの経験があればこのセクションの残りの部分は非常に身近に感じるでしょう。
ジェネリック関数は、その名前と引数のリストを指定して、(通常は)実装されていない抽象操作(abstract
operation)を指定します。引数のいくつかの固有クラスにたいする実際の実装はメソッド(methods)により提供され、これは個別に定義されるべきです。ジェネリック関数を実装するそれぞれのメソッドはジェネリック関数としてとして同じ名前をもちますが、そのジェネリック関数で定義された引数のスペシャライジング(specializing)により、メソッドの定義はどの種類の引数を処理可能かを示します。これらの引数スペシャライザー(argument
specializers)は多少の差はあれ特化したものにできます。たとえばstring
型はsequence
のようなより一般的な型より特化した型です。
C++やSimulaのようなメッセージベースのOO言語と異なり、ジェネリック関数を実装するメソッドはクラスに属さずに、それらが実装するジェネリック関数に属することに注意してください。
ジェネリック関数が呼び出されると、呼び出し側に渡された実際の引数と各メソッドの引数スペシャライザーを比較することにより、適用可能なメソッドを呼び出します。その呼び出しの実際の引数がメソッドのスペシャライザーと互換性があれば、そのメソッドが適用可能です。複数のメソッドが適用可能ならば、それらは以下で説明する特定のルールにより合成されて、その組み合わせが呼び出しを処理します。
このマクロは指定したnameとargumentsでジェネリック関数を定義する。bodyが与えられたなら、それは実装のデフォルトを与える。(常に与えられるべきであるが)documentationが与えられたなら、それは(:documentation
docstring)
の形式でそのジェネリック関数のドキュメント文字列を指定する。オプションのoptions-and-methodsは以下のフォームのいずれかを指定できる:
(declare declarations)
declare
フォームで説明するようなdeclareフォーム。
(:argument-precedence-order &rest args)
このフォームは適用可能なメソッド合成にたいするソート順に影響を与える。合成において2つのメソッドを比較する際、メソッドの引数は通常は左から右に試験されて、引数スペシャライザーがより特化した最初のメソッドが他のメソッドより前になる。このフォームで定義された順序はそれをオーバーライドして、左から右ではなくこのフォームの順に応じて試験される。
(:method [qualifiers…] args &rest body)
このメソッドはcl-defmethod
が行うようなメソッドを定義する。
このマクロはnameと呼ばれるジェネリック関数の、特定の実装を定義する。実装コードはbodyで与えられる。もし与えられたらdocstringはそのメソッドのドキュメント文字列である。リストargumentsはジェネリック関数を実装するすべてのメソッドで等しく、その関数の引数リストとマッチしなければならず、(arg
spec)
という形式の引数スペシャライザーを提供する。ここでargはcl-defgeneric
呼び出しで指定された引数名、specは以下のスペシャライザーフォームのいずれかであること:
type
このスペシャライザーは、引数がtypeのいずれかであることを要求する。typeは以下で説明する型ヒエラルキーのいずれかの型である。
(eql object)
このスペシャライザーは、引数がobjectとeql
であることを要求する。
(head object)
引数はcar
がobjectとeql
であるようなコンスセルでなければならない。
struct-type
引数はcl-defstruct
(Structures in Common Lisp Extensions for
GNU Emacs
Lispを参照)で定義されたstruct-typeという名前のクラス、またはその子クラスのインスタンスでなければならない。
メソッド定義は新たな引数リストのキーワード&context
を使用できる。これはメソッド実行時に環境をテストする余分なスペシャライザーを導入する。このキーワードは必須の引数リストの後、かつすべての&rest
と&optional
キーワードの前に記述すること。&context
スペシャライザーは正規の引数スペシャライザー(expr
spec)と非常によく似ているが、exprはカレントコンテキストで評価される式であり、specは比較対象となる値となる。たとえば&context
(overwrite-mode (eql
t))
は、メソッドをoverwrite-mode
がオンのとだけ適用可能にする。&context
キーワードの後には任意個数のコンテキストスペシャライザーを続けることができる。コンテキストスペシャライザーはジェネリック関数の引数signatureの一部ではないので、これらを必要としないメソッドでは省略できる。
型スペシャライザー(arg type)
は以下のリストのシステム型(system
types)のいずれかを指定できる。親の型が指定されたときは、型がより特化した子型、孫型、曾孫型、...のいずれかであるような任意の引数も互換となるだろう。
integer
親型: number
。
number
null
親型: symbol
。
symbol
string
親型: array
。
array
親型: sequence
。
cons
親型: list
。
list
親型: sequence
。
marker
overlay
float
親型: number
。
window-configuration
process
window
subr
compiled-function
buffer
char-table
親型: array
。
bool-vector
親型: array
。
vector
親型: array
。
frame
hash-table
font-spec
font-entity
font-object
として表されるオプション要素extraによって、同一のspecializerとqualifierにたいして、stringで区別されるメソッドを追加できる。
オプションのqualifierは複数の適用可能なメソッドの合成を許容する。与えられなければ定義されるメソッドはprimary(主)メソッドとなり、スペシャライズされた引数にたいする主要な実装の提供に責任を有する。qualifierとして以下の値のいずれかを使用してauxiliary(副)メソッドも定義できる:
:before
このauxiliaryメソッドはprimaryメソッドの前に実行される。より正確にはすべての:before
メソッドは、より特化したメソッドが最初になる順で、primaryメソッドの前に実行される。
:after
このauxiliaryメソッドはprimaryメソッドの後に実行される。より正確にはすべてのこの類のメソッドは、より特化したメソッドが最後になる順で、primaryメソッドの後に実行される。
:around
このauxiliaryメソッドはprimaryメソッドの代替えとして実行される。この類のメソッドでもっとも特化したものが他のメソッドより前に実行される。このようなメソッドは他のauxiliaryメソッドやprimaryメソッドを呼び出すために、通常は以下で説明するcl-call-next-method
を使用する。
cl-defmethod
を使用して定義した関数をインタラクティブにすることはできない。つまりinteractive
フォームを追加してコマンドにすることはできない(コマンドの定義を参照)。多相型コマンド(polymorphic
command)が必要なら、cl-defgeneric
とcl-defmethod
を通じて定義した多相型関数(polymorphic
function)を呼び出す通常のコマンドを定義することを推奨する。
ジェネリック関数が呼び出されると、毎回その関数にたいして定義された適用可能なメソッドを合成することによってその呼び出しを処理するeffectiveメソッド(effective method)を構築します。適用可能なメソッドを探してeffectiveメソッドを生成するプロセスはdispatchと呼ばれます。その呼び出しの実際の引数と互換性があるスペシャライザーをもつすべてのメソッドが、互換性のあるメソッドです。すべての引数がスペシャライザーと互換でなければならないので、それらはすべてメソッドが適用可能かどうか判定します。複数の引数に明示的に特化したメソッドをmultiple-dispatchメソッド(multiple-dispatch methods)と呼びます。
適用可能なメソッドはそれらが合成される順にソートされます。最左の引数スペシャライザーがもっとも特化したものであるようなメソッドが、順序の最初になります(上述したようにcl-defmethod
の一部として:argument-precedence-order
を指定することによりこれをオーバーライドできる)。そのメソッドのbodyがcl-call-next-method
を呼び出すと、もっとも特化した次のメソッドが実行されます。適用可能な:around
メソッドがあれば、それらのうちもっとも特化したメソッドが実行されます。そのメソッドはより特化していない任意の:around
メソッドを実行するために、cl-call-next-method
を呼び出すべきです。次に:before
メソッドがその特化した順に、その後にspecificityメソッドが実行されます。そして後に:after
メソッドがその特化した順と逆順で実行されます。
primaryメソッドか:around
auxiliaryメソッド内のレキシカルbody内で呼び出されると、同じジェネリック関数にたいして適用可能な次のメソッドを呼び出す。通常これは引数なしで呼び出され、これは次の適用可能なメソッドを呼び出すメソッドが、呼び出されたときと同じ引数で次のメソッドを呼び出すことを意味する。それ以外ならかわりに指定された引数が使用される。
primaryメソッドか:around
auxiliaryメソッドのレキシカルbody内からこの関数を呼び出したときは、呼び出す次のメソッドが存在すれば非nil
をリターンする。