Macro DEFINE-CONDITION
define-condition name (parent-type*) ({slot-spec}*) option*
=> name
slot-spec::= slot-name | (slot-name slot-option)
slot-option::= [[{:reader symbol}* |
{:writer function-name}* |
{:accessor symbol}* |
{:allocation allocation-type} |
{:initarg symbol}* |
{:initform form} |
{:type type-specifier} ]]
option::= [[(:default-initargs . initarg-list) |
(:documentation string) |
(:report report-name) ]]
function-name::= {symbol | (setf symbol)}
allocation-type::= :instance | :class
report-name::= string | symbol | lambda expression
name - シンボル
parent-type - コンディションの型の名前であるシンボル。 もしparent-typeが指定されなかったときは、 parent-typesはデフォルトで(condition)です。
default-initargs - キーワードと値のペアのリスト
slot-spec - スロットの名前か、 slot-nameに続いてゼロか複数のslot-optionを含むリスト。
slot-name - スロットの名前(シンボル)か、 スロットの名前のリストか、 【翻訳者注釈】名前とスロットのフォームのペアのリスト。
option - 下記のどれかのもの
:reader
:readerは1つのスロットに対し1つ以上指定できますが、 nilは指定できません。:writer
:writerは1つのスロットに対し1つ以上指定できますが、 ジェネリック関数の名前でなければいけません。:accessor
:accessorは1つのスロットに対し1つ以上指定できますが、 nilは指定できません。:allocation
:allocationは1つのスロットに対し1つ指定できます。 :allocationが指定されなかったときのデフォルト値は:instanceです。:initarg
:initargは1つのスロットに対し1つ以上指定できます。:initform
:initformは1つのスロットに対し1つ指定できます。:type
:typeは1つのスロットに対し1つ指定できます。:documentation
:documentationは1つのスロットに対し1つ指定できます。:report
:reportは1つ指定できます。※翻訳者注釈:名前とスロットフォームは許可されないかもしれません。
define-conditionは新しいコンディションの型を定義します。 それはnameという名前であり、 parent-typeによって名付けられた型か あるいは型の集合のサブタイプになります。 各parent-type引数は、新しいコンディションの 直接的なスーパータイプとして指定されます。 新しいコンディションは各直接的なスーパータイプから スロットとメソッドが継承されます。
もしスロットに名前とスロットのフォームのペアが指定されたとき、 スロットのフォームは make-conditionに明に値が指定されなかったときの デフォルト値を提供するためにそのformを評価できます。 もしスロットフォームが指定されなかったときは、 そのスロットの値は実装依存の方法で初期化されます。 (※翻訳者注釈:名前とスロットフォームは許可されないかもしれません)
もしtypeが定義されるとき、 その型が同じ名前のスロットを持つ他のを継承するとき、 そのコンディションではただひとつのスロットだけが確保され、 そのスロットフォームはparent-typeによって継承された 他のスロットフォームによって上書きされます。 もしスロットフォームが指定されなかったときは、 継承されたスロットフォームは(あるならば)まだ見ることができます。
アクセッサはdefclassが使われたときと 同じルールに従って生成されます。
slot-optionの定義は下記の通り。
:reader
:readerスロットオプションは、修飾子がないメソッドを :reader引数で指定した名前のジェネリック関数に定義し、 その関数はスロットの値を読み込みます。 :intiform
:initformスロットオプションは、そのスロットが初期化されるときの デフォルトの初期値フォームを提供するときに使われます。 このフォームはスロットが初期化するときに毎回評価されて使われます。 このフォームが評価されるときのレキシカルな環境は、 define-conditionフォームが評価されたときの レキシカルな環境です。 このレキシカルな環境は、 変数と関数の両方を参照することに注意してください。 ローカルスロットの動的環境は、 make-conditionが呼び出されたときの動的環境です。 共有スロットの動的環境は、 define-conditionフォームが評価されたときの動的環境です。 :initarg
:initargスロットオプションは、引数のシンボルを 初期化引数の名前にするように宣言します。 この初期化引数によって指定したスロットが初期化されます。 もしinitialize-instanceの呼び出し時に この初期化引数が値を持っているときは、 指定のスロットに値がか格納され、 そのスロットオプションに:initformがあるなら それは評価されません。 もし指定したスロットで定義された 初期化引数が値を持っていないときは、 もし指定があるなら:initformスロットオプションに従った値で 初期化されます。 :type
:typeスロットオプションは、スロットの内容が 常に指定された型であることを指定します。 この宣言は、このコンディションの型のオブジェクトに適用される ジェネリック関数であるリーダーの返却値の型を 効率よく宣言できます。 このスロットに安全ではない型の値を格納しようとしたときの 結果は未定義です。 :default-initargs
defclassと同じように扱われます。 :documentation
:documentationスロットオプションは、 そのスロットのドキュメント文字を提供します。 :report
*print-escape*がnilに指定されているときはいつでも print-objectメソッドを仲介します。 そのコンディションの型Cの定義に(:report report-name)が指定されたときは、 それは下記のものと同等です。(defmethod print-object ((x c) stream)
(if *print-escape* (call-next-method) (report-name x stream))):report (report-name)で指定された値が シンボルかラムダ式のときは、 それはfunctionを受け付けなければなりません。 (function report-name)が現在のレキシカルな環境内で評価されます。 それは2つの引数のコンディションとストリームを受け取り、 コンディションの定義をストリームに印刷するような 関数でなければなりません。 この関数は*print-escape*がnilのときは いつでもコンディションの印刷のときに呼び出されます。 (lambda (condition stream)
(declare (ignore condition))
(write-string report-name stream)):report関数の内部でスロットのアクセッサを 使用することが許されます。 もしこのオプションが指定されなかったときは、 このコンディションの型がどのようにして 報告されるのかについての情報は、 parent-typeから継承されます。 明に初期化されなかったスロットか、 あるいはデフォルト値を与えられなかったスロットについて 読み込みをしようとしたときの結果は未定義です。
スロットへの代入をsetfによって 行おうとしようとしたときの結果は未定義です。
もしdefine-conditionフォームがトップレベルフォームに現れたとき、 コンパイラーはその型の名前を有効なものとして認識しなければならず、 そのコンパイルされているファイル内の 後続のdefine-conditionが その他のコンディションのparent-typeに そのコンディションの型が参照できるようにしなければなりません。
下記の例はpeg/hole-mismatchというコンディションの型の定義であり、 これはblocks-world-errorと呼ばれるコンディションの型を継承しています。
(define-condition peg/hole-mismatch
(blocks-world-error)
((peg-shape :initarg :peg-shape
:reader peg/hole-mismatch-peg-shape)
(hole-shape :initarg :hole-shape
:reader peg/hole-mismatch-hole-shape))
(:report (lambda (condition stream)
(format stream "A ~A peg cannot go in a ~A hole."
(peg/hole-mismatch-peg-shape condition)
(peg/hole-mismatch-hole-shape condition)))))新しい型はpeg-shapeとhole-shapeというスロットを持っており、 よってmake-conditionは:peg-shapeと:hole-shapeという キーワードを受け付けます。 リーダーであるpeg/hole-mismatch-peg-shapeとpeg/hole-mismatch-hole-shapeは その型のオブジェクトを受付け、 :reportはその型の情報を出力します。
次のフォームはmachine-errorという名前のコンディションの型を定義しており、 これはerrorを継承しています。
(define-condition machine-error
(error)
((machine-name :initarg :machine-name
:reader machine-error-machine-name))
(:report (lambda (condition stream)
(format stream "There is a problem with ~A."
(machine-error-machine-name condition)))))次の定義の構築は、machine-errorのサブタイプとして 定義された新しいエラーのコンディションとして定義されたものであり、 機械が利用できないときに使われるものです。
(define-condition machine-not-available-error (machine-error) ()
(:report (lambda (condition stream)
(format stream "The machine ~A is not available."
(machine-error-machine-name condition)))))この定義はまだより詳しいコンディションが定義でき、 machine-not-available-errorは machine-nameの初期化フォームを提供していますが、 しかし新しいスロットも報告情報も提供していません。 ただmachine-nameのスロットのデフォルト初期化フォームだけ指定しています。
(define-condition my-favorite-machine-not-available-error
(machine-not-available-error)
((machine-name :initform "mc.lcs.mit.edu")))注意してほしいのは、:reportオプションが与えられなくても、 その型の情報を報告する際には 継承されたmachine-not-available-errorの情報が使われます。
(define-condition ate-too-much (error)
((person :initarg :person :reader ate-too-much-person)
(weight :initarg :weight :reader ate-too-much-weight)
(kind-of-food :initarg :kind-of-food
:reader :ate-too-much-kind-of-food)))
=> ATE-TOO-MUCH
(define-condition ate-too-much-ice-cream (ate-too-much)
((kind-of-food :initform 'ice-cream)
(flavor :initarg :flavor
:reader ate-too-much-ice-cream-flavor
:initform 'vanilla ))
(:report (lambda (condition stream)
(format stream "~A ate too much ~A ice-cream"
(ate-too-much-person condition)
(ate-too-much-ice-cream-flavor condition)))))
=> ATE-TOO-MUCH-ICE-CREAM
(make-condition 'ate-too-much-ice-cream
:person 'fred
:weight 300
:flavor 'chocolate)
=> #<ATE-TOO-MUCH-ICE-CREAM 32236101>
(format t "~A" *)
>> FRED ate too much CHOCOLATE ice-cream
=> NILなし。
なし。
make-condition, defclass, 9.1. コンディションシステムの説明
なし。