デフォルトコンポジションとデフォルトメソッド

[id:lethevert:20051002:p6]
[id:lethevert:20051003:p5]
[id:lethevert:20051008:p1]
昔、この辺で、継承についてあれこれ考えていたのですが、

「インターフェース」と「クロージャ」さえあれば、「継承」は要らない

とかって書いていました。
数ヶ月ぶりにそれを振り返って、その当時考えていたのとはちょっと違う意味で、その命題に納得しているのですが、それを説明するのはちょっと難しいので、まずはタイトルのような概念を定義してみようと思います。

デフォルトコンポジション

下のMixinの記事にあるように、継承をコンポジションで模することは可能なわけなのですが(そして、その実現のためにクロージャ*1を利用しているのですが)、このコンポジションは本物の継承とどう違うのかということを考えてみます。
最大の違いは、コンポジションにアクセスするには、フィールドを介してアクセスしなければいけないのに対して、継承にアクセスするにはフィールドを介さなくてもよいということです。
また、継承ではメソッドやフィールドのオーバーライド(あるいは隠蔽)が発生しますが、コンポジションは完全に名前空間が分離するのでそのようなことは起こりません。
しかし、逆に考えると、(型付けの問題を無視すれば)、コンポジションと継承の違いは、その2点しかありません。これは、非常に興味深い点です。
ここで「無名のフィールド」というものが存在すると仮定して、それを利用してコンポジションを作ることを考えてみてください。つまり、

cat.name_ad.reverse()

とアクセスしていたものを、"name_ad"が無名のフィールドとなることで、

cat.reverse()

と書けるようになると考えてみるのです。すると、「無名のフィールドを介したコンポジション」は「継承」と全く変わらないことに気づくと思います。つまり、「継承」を「無名のフィールドを介したコンポジション」を使って実現可能だということです。
「無名の」とは本当に名前がない必要はなく、ただデフォルトのマークがあり、そのデフォルトのマークをコンパイラが理解すれば、十分だといえます。つまり「デフォルトコンポジション」が使える言語ならば、継承を特別なものとして取り扱う必要はないということになります。(単一継承はデフォルトコンポジションを1つしか取れないということで、多重継承はデフォルトコンポジションを複数取れると見ることができます。)
さらに、継承をデフォルトコンポジションであると考えれば、実行時に継承の親を取り替えるというおまけ機能も実現可能になります。

デフォルトメソッド

こちらは、継承とは関係ないのですが、デフォルトコンポジションから連想して思いついたので、並べて書いておきます。
クラスにデフォルトメソッドを指定できれば、オブジェクトと関数の区別が、見た目上も変わらなくなるということです。つまり、

aFunctionObject.eval()

と書くところを、evalをデフォルトメソッド指定しておけば、

aFunctionObject()

と呼び出せるということになります。
こちらは、デフォルトのマークをコンパイラが理解するだけで実現可能になります。

JavaScript

ところで、ここまで読んで、聡い読者の方はもうお気づきかと思いますが、上のデフォルトコンポジションとデフォルトメソッドというアイデアは、JavaScriptが既に採用しています。
しかし、Javaのような純で静的な言語で、もっと整理された概念として採用されると嬉しいなと思います。(Javaをデフォルトコンポジションに変えるのは、無理だろうな)