(rontolisp) docs
← 特殊形式

defmethod

(defmethod name [qualifier] (param... ) body...)

総称関数 name にメソッドを追加し、名前シンボルを返します(先行する defgeneric がなければ総称関数を暗黙に作ります)。specializer は任意の必須引数に付けられ、(var specializer) と書きます:

  • (var (eql literal)) — 引数がそのリテラル(キーワード、クォートされたシンボル、数値、文字)のときにマッチ
  • (var class-name)defclass クラスとそのサブクラスのインスタンスにマッチ
  • (var struct-name)defstruct 型のインスタンスにマッチ(ディスパッチャは構造体述語と同じインスタンスタグをテストします)
  • (var type-name) — 組み込み型(integerfloatnumberstringsymbolkeywordcharacterconslistnullhash-tablefunctionpathnamepackage など)にマッチ。package 引数は typep がパッケージとみなすものにちょうどマッチし、keyword/symbol より先に判定されます。そのため「package メソッド + find-package を呼んで再帰する非特定化メソッド」という designator のイディオムが停止します
  • (var t) または素の var — デフォルトメソッド

呼び出しはマッチする最も特定的なメソッドを実行します: まず eql メソッド、次にクラスメソッド(サブクラスがスーパークラスより先)、次に組み込み型(integer のようなサブタイプが number のようなスーパータイプより先)、最後にデフォルトメソッドの順で、マッチがなければエラーを通知します。同じ specializer を再定義すると以前のメソッドを置き換えます。本体はドキュメント文字列と (declare ...) で始められます(どちらも無視されます)。

setf メソッド

name には関数名 (setf reader) も書けます: メソッドは readersetf 関数の一部となり、(setf (reader arg...) value) は新しい値を先頭パラメータとして(CL の setf 関数の引数順)ディスパッチします。defclass:accessor と同名の setf メソッドは、アクセサの writer メソッドを隠すのではなくマージされます。#'(setf reader) は第一級関数としての writer です。(defgeneric (setf reader) ...) も同様に動作し、インラインの (:method ...) 節も使えます。

メソッド修飾子と call-next-method

ラムダリストの前に :before:after:around修飾子を置くと補助メソッドを追加できます(標準メソッド結合)。1 回の呼び出しでは:

  • 適用される :around メソッドが最も特定的なものから順に実行され、それぞれが残りをラップします。
  • 次に :before メソッドが最も特定的なものから順に副作用として実行されます。
  • 次に最も特定的な基本(修飾子なし)メソッドが実行され、その値が結果になります。
  • 最後に :after メソッドが最も特定的でないものから順に副作用として実行されます。

短形式の :method-combination のもとでは修飾子の集合が変わります。プライマリメソッドは代わりに結合名を修飾子として持ち((defmethod total + ((x account)) ...))、:around は従来どおり全体をラップし、:before/:after は拒否されます。

基本メソッドや :around メソッドの中では、(call-next-method) が次に特定的でないメソッドを呼び出し(現在の引数を渡し、(call-next-method arg...) と書けば新しい引数を渡します)、(next-method-p) はそのようなメソッドが存在するかを返します。次のメソッドがない状態で call-next-method を呼ぶとエラーになります。

ライトサブセット: &key はエラー、標準メソッド結合はクラスメソッドとデフォルトメソッドについてサポートされます(eql や組み込み型の specializer を持つ :around/:before/:after は、同じ specializer の基本メソッドとデフォルトメソッドのみと結合します)。コンパイルパスでは defmethod はトップレベルフォームとしてのみサポートされ、コンパイルされたプログラムのメソッド集合はコンパイル時に固定されます。

組み込み関数名へのメソッド定義

組み込み関数の名前(closeopen-stream-pstream-element-type など)にメソッドを定義すると、その組み込み関数が総称関数のデフォルトメソッドになります。specializer に指定したクラスのインスタンスではメソッドが実行され、それ以外の引数では組み込みの動作がそのまま残ります(最も限定的でない基本メソッドからの (call-next-method) も組み込みに到達します)。そのため自作のストリームクラスに close メソッドを定義しても、実際のファイルストリームに対する (close stream) は動作し続けます。ユーザーがデフォルトメソッド(specializer なし)を定義した場合は、そちらが組み込みを完全に置き換えます。

ライトサブセット: これはすべてのバックエンドで動作します — コンパイルパスではその名前の呼び出しが生成されたディスパッチャを経由し、フォールスルーが元の組み込みになります。対象はネイティブの組み込み関数に裏付けられた名前のみです。展開として実装されている名前(mapcarsortformat など)にはメソッドを定義できず、組み込み名への単純な defun はコンパイルパスでは引き続き無視されます。