型理論や仕様論は一番好きな話題かもしれないな(背後にあるインスティチューションが好きなんだろうが)。続き物にする気はなかったのだけど、また型理論の話を少し。
"types as specifications/theories"の立場では、サブタイプ/スーパータイプの定義は複雑になります。
問題は、サブタイプ/スーパータイプ概念が、extendsだけで尽きているわけではないことです。まだ説明すべきことが残っているのです。
と書いたけど、extendsだけも分かること/説明できることは随分あります。それと、少し工夫すると、サブタイプ/スーパータイプ概念をextendsだけで説明することもできるのだった(“工夫”の部分があるから、簡単かどうかは別問題だけど)。
「もっと型理論」で説明した意味で「型」「仕様」「extends」「拡張」などの言葉を使うとして、いま T2 extends T1
だとしましょう -- ここで、T1, T2は型(つまり仕様、それは、インターフェース+制約)です。これは次のどちらか、または両方であることを意味します。
- [インターフェースの拡張] T2のインターフェース部分は、T1のインターフェース部分を拡張(extend)している。
- [制約の拡張(強化)] T2の制約部分(ホーア制約式の集まり)は、T1の制約部分を拡張している。
このとき、「T2はT1のサブタイプである(T1はT2のスーパータイプである)」と言ってかまいません。型階層を図に描くとき、普通はスーパータイプを上のほうに描くので、T1は上位、T2は下位だと言ってもいいでしょう。
下層にいくほど実装は大変になる
この世で一番簡単な仕様(型と同義語)は次です。
spec Any { interface { } /* 制約はない */ }
インターフェースは空っぽで、制約式もなしです。空っぽのインターフェースは「メソッドを持ってはいけない」ということではなくて、「必須のメソッドは特にないよ」という意味なので、どんなクラスでもこの仕様を満たします。つまり、Anyの実装は全然気を使う必要がない、まったくお気楽・勝手気ままなのです。
spec AB { interface { int a(); void b(); } /* 制約はない */ }
これはインターフェースが規定されているので、なんでもいいがとにかくaとbの実装は必須です。aとbをどう実装しようと勝手、という点ではやっぱり気楽ですが、仕様を満たす手間がゼロというわけではありません。
spec AB3 { interface { int a(); void b(); } [1] (true){}(-1 <= a() && a() <= 255); [2] Any(int x) (a() == x){}(a() == x); [3] Any(int x) (a() == x){a();/*値は捨てる*/}(a() == x); }
こうなると、だいぶ気を使います。仕様を満たす労力がそれなりに発生します。もっとメソッドが増えたり、もっと制約式が増えたりすると、どんどん大変になります。
型階層の上下は、実装に使う注意力や労力の多寡
仕様で規定されるメソッドや制約が増えれば増えるほど、それを満たす実装を作るために気を使い、手間がかかることになります。spec Any
が定義する型では、この仕様を満たすための注意力や労力はゼロですよね。そして、この型が型階層の最上位(ルート)に位置します。一番エライけど無視可能な奴だ。
で、まとめれば、こういうこと:
- サブタイプになるほど定義が複雑になる。
- サブタイプになるほど、その実装にたくさん気を使い、手間もかかるようになる。