Posted 2021-03-22 01:47:43 GMT
MOPでメタクラスを定義した場合などに定義が必要になるvalidate-superclass
ですが、処理系によって定義が必要であったりなかったりするので、実際のところどういう動作が正しいのか改めて確認してみました。
validate-superclass
を定義する意味メタクラスが違う二つのクラスの間で継承関係が成立するかどうかは分からないのでデフォルトでは継承関係は成立しないとしていて、成立させたい場合は明示する仕組みというのが簡単な説明です。
この「デフォルトでは継承関係は成立しない」というのをvalidate-superclass
で表現していて、成立させる場合にはT
を返すメソッドを定義します。
(defclass my-class (standard-class)
())(validate-superclass (class-prototype (find-class 'my-class))
(class-prototype (find-class 'standard-class)))
→ nil
この状態で、my-class
をメタクラスとするクラスmy-object
を定義する場合、my-object
はオブジェクトの表現としてstandard-object
を継承して利用するのがデフォルト動作(省略時)なので、
(defclass my-object (standard-object)
()
(:metaclass my-class))
のようなものを書いた場合、
(validate-superclass (class-prototype (find-class 'my-class))
(find-class 'standard-object))
のようなチェックが一連のスーパークラスで実施され、全てがT
でなければ、エラーとなります。
処理系ごとのvalidate-superclass
の動作の違いですが、下記のようになります。
(validate-superclass (class-prototype (find-class 'my-class))
(find-class 'standard-object))
→ nil
AMOPに記載の通りの判定ですが、
あたりがそういう挙動で、validate-superclass
をちゃんと書いてやる必要があります。
standard-class
のサブクラスで、スーパーメタクラスがstandard-class
の場合は互換性あり(validate-superclass (class-prototype (find-class 'my-class))
(find-class 'standard-object))
→ T
あたりがこの挙動です。
この挙動であれば、validate-superclass
を書かなくて良さそうにも思えますが、メタクラスがstandard-class
の別のサブクラス同士だと継承関係がない場合があるので、その場合はvalidate-superclass
を書いてやる必要があります。
具体的には、下記のコードのような状況でvalidate-superclass
の定義が必要になります。
(defclass my-class/ (standard-class)
())(defclass my-object/ (standard-object)
()
(:metaclass my-class/))
(validate-superclass (class-prototype (find-class 'my-class/))
(find-class 'my-object))
→ nil
(defmethod validate-superclass ((c my-class/) (s my-class))
T)
(validate-superclass (class-prototype (find-class 'my-class/))
(find-class 'my-object))
→ T
;; 上記の定義がなければエラー
(defclass my-object// (my-object)
()
(:metaclass my-class/))
standard-class
のサブクラスなら互換性ありあたりがこの挙動です。
メタクラスがstandard-class
のサブクラス同士であれば、validate-superclass
の定義を書く必要はありません。
これはこれで便利な挙動で、validate-superclass
の定義を書くことは殆ど無くなるのは良いのですが、この挙動が災いしてAllegro CLのコードの移植性の無さの一因になっている気がします。
上記のように処理系によってデフォルトの挙動が違いますが、互換性があることを明示するvalidate-superclass
のコードがあっても挙動を変えることはないので、AMOP準拠で全部明示しておくのが吉かなと思います。
ANSI CL規格では、互いに素である型が定義されていますが、
defclass
、define-condition
、defstruct
で継承関係を定義した型以外は互いに素であるとしています。
integer
とcons
の間では継承関係を考えようとは思わないのですが、メタクラスをカスタマイズする場合は、メタクラスが異なるのみで他の挙動は継承したいことがほとんどかと思います。
validate-superclass
は用途が限定されている割には機能としては汎用的なのですが、もともとはcheck-super-metaclass-compatibility
という名前だったようです。
途中で、valid-superclass-p
等の名前になったりもしたようですが、1990年頃、validate-superclass
で落ち着き現在に至る様子。
check-super-metaclass-compatibility
はvalidate-superclass
よりも判定が厳しく、デフォルトの挙動は双方のメタクラスがeq
の場合のみT
としていたようです。
現在のvalidate-superclass
もcheck-super-metaclass-compatibility
の目的に使うことが殆どですが、CMUCLやSBCLでは互換性の判定用に組み込みクラスについても非互換性のリストをもっているので、
(validate-superclass (find-class 'null)
(find-class 'cons))
→ nil
のように判定します。
他の処理系は、大体のところはstandard-class
の範疇の判定しか想定していないようなのでT
を返しますが、こんな動作でも問題ない程度には汎用的には使われていないということなのかもしれません……。
validate-superclass
について掘り下げてみましたが、validate-superclass
は用途が限定的ですし、考えるほどcheck-super-metaclass-compatibility
という名前のままでも良かったのではないかと思えてきます。
■
HTML generated by 3bmd in LispWorks 7.0.0