多態のメカニズム分類

http://shinh.skr.jp/h/?DuckTyping
いろいろ考えているうちにごっちゃになってきました。
混乱の中、tepmlateclass u{...};があったとして、tがメンバ関数を用意する方法と、uの中でtのメンバ関数を呼ぶ方法と、uをインスタンス化する方法は、全部別の話ではないかと思えてきました。
まず多態というメカニズムは、その性質上、共通基底「何か」がないと成立しません。Object型でもバリアント型でもvoid *でもいいです。型名を表すコンパイル時変数でもいいです。

  • 共通基底「何か」を渡す側
  • 共通基底「何か」を使う側
  • その結果呼ばれる側

この3つに分けられるのではないかなと。
何をどこで指定してどこが拘束されるのかを見ていけばいいかなあ……。intrusiveは呼ばれる側の問題、dynamicは使う側の問題……。
C++のtemplate
クラステンプレートuによるコンパイル時多態。
共通基底「何か」は、typename tですね。静的な型変数ということになると思います。
typename tを渡す側は、u v;とインスタンス化する側です。コンパイル時に解決され、実行時に呼び出される関数は固定されています(static)。
typename tを使う側は、templateclass u{...}です。これが、tの要件を決定します。例えば、デストラクタが必要、など。
その結果呼ばれる側は、例えばt::~t()です。これは、tにくっついており、tの宣言時に宣言されていなければなりません(intrusive)。ただし、u用であることを明記する必要はありません(implicit)。
そこでこれを(shinichiro.hさんの分類とは異なりますが)、static-intrusive-implicitとしたいと思います。

Adaのgeneric
generic package Gによるコンパイル時多態。
共通基底「何か」は、type T(<>) is abstract limited private;です。実体を使う必要があるならabstractを取って、固定サイズの型として使いたいなら(<>)を取って、代入したいならlimitedを取って……と制約を緩めていくことができますが、とりあえず一番多くの種類の型を受け取れる宣言ということでtype T(<>) is abstract limited private;です。長いので以下Tとだけ書きます。
Tを渡す側は、package I is new G(T);とインスタンス化する側です。静的です。(static)
Tを使う側は、Gです。Tの要件は、上記制約と、withなんたら宣言で明示します。例えば、with procedure Finalize (It : in out T);が必要、など。
このFinalizeは、C++のtemplateとは異なり、インスタンス化する側が決定します。package I is new G(T, Finalize => Close);と書けば(explicit)、Closeが使われます。CloseはTの宣言とは関係ないサブプログラムとして用意できます(nonintrusive)。
そこでこれをstatic-nonintrusive-explictとしたいと思います。

……まだimplicit/explictが少々混乱してます。Gの引数としてTやCloseを明記するのがexplictであれば、uとtを明記しているのはexplicitにはならないのでしょうか……なると思います。うむ、そもそもimplicit/explictなんてexplictの場合はめんどくさいというだけで機能的な差異ではないので、渡す側使う側呼ばれる側で多数決取ってimplicit/explicitを決めましょうそうしましょう。

  • C++のクラステンプレートによる多態は、static-intrusive-implicit(e/i/i)。
  • C++の関数テンプレートによる多態は、static-intrusive-implicit(i/i/i)。
  • Adaのgeneric packageによる多態は、static-nonintrusive-explict(e/e/i)。

Haskellの型クラス
class C t where...による実行時多態。実行時ですよね。Existential typeを使えばinterfaceのようにも使えるっぽいですし。Haskell'98の範囲ですと静的っぽいですけど。
共通基底「何か」は、t。
tを渡す側は、単にC 'aを取る関数にinstance C u where...宣言された型uの値を渡している箇所ということになります。instance宣言の内容にあわせて多態します(dynamic)。
instance宣言はuと別に書けますのでnonintrusive。instance宣言を明記する以上explict。でも渡す側呼ぶ側が型推論のお陰でimplicitですので多数決によってimplicitです。(←ひどい分類方法と自分で思う)

  • Haskellの型クラスによる多態は、dynamic-nonintrusive-implicit(i/i/e)。

Haskell'98の範囲ですと、staticと見なしていいかも。

同じnonintrusiveでも、AdaとHaskellには、dynamic/static以外にもうひとつ差があります。Haskellは、instance C u where宣言をひとつの実行ファイル中に複数用意できません。Adaは、インスタンス化する側が組み合わせを好き勝手にできます。
この差を表現したいです。
例えば他にも、Eiffelは同じ基底クラスを反復継承して使い分けることができますが、interfaceを一度継承したらそれっきりな言語と区別したいじゃないですか。あとはC++0xのconcept_mapあたり……翻訳単位変えたら使い分け可能っぽいですし。
この違いをsingleton/multipleと書きましょう。
(あとAdaのはgeneric packageとgeneric procedure/functionを区別する意味も無いのでgenericとだけ書きます)

  • C++のクラステンプレートによる多態は、static-intrusive-implicit(e/i/i)-singleton。
  • C++の関数テンプレートによる多態は、static-intrusive-implicit(i/i/i)-singleton。
  • Adaのgenericによる多態は、static-nonintrusive-explict(e/e/i)-multiple。
  • Haskellの型クラスによる多態は、dynamic(static)-nonintrusive-implicit(i/i/e)-singleton。

dynamicが出てきたので、C++/Adaの通常の継承による多態の方も併記しておきます。当然dynamic-intrusive-explict-singletonですね。

  • C++の継承による多態は、dynamic-intrusive-explict(i/e/e)-singleton。
  • Adaのクラスワイド型による多態は、dynamic-intrusive-explict(i/e/e)-singleton。

Eiffelの継承による多態
既に述べましたようにEiffelは反復継承を解決できる、これが一番違います。dynamic-intrusive-explict-multipleです。

  • Eiffelの継承による多態は、dynamic-nonintrusive-explict(i/e/e)-multiple。

Eiffelの総称型による多態

  • Eiffelの総称型による多態は、static-intrusive-explicit(e/e/e)-singleton。

Eiffelには制約された総称がありますが、引数型Tが特定のメンバ関数を持っているのを要求するのも、特定の基底型を持っているのを要求するのも、大差ないですよね。C++でも要求しようと思えばできますし。基底型によって制約を表しているため基底型の型名がexplicitになっているところがC++との差です。

……疲れてきた。LLとかどうでもいい。せ、せめてObjective-Cを分類しておかねばっ。

Objective-Cの多態
Objective-Cにはカテゴリという機能があって、メソッドや、プロトコルを後付けできます。ただしメソッド名が衝突するとダメです。名前空間も汚染してしまいますので、nonintrusiveとするには?が残ります。括弧付きで書いておきます。
Objective-Cにはプロトコル(interface)の他に、非形式プロトコルというのが使われていて、型無しLLのDuck Typingと同じものです。基本id型でなんでもできますので、型名を書くほうがオプションです。なのでimplicit。当然dynamic。

  • Objective-Cの多態は、dynamic-intrusive(nonintrusive)-implicit(i/i/iただしオプションで明示可)-singleton。

存在しないメッセージを送っても実行時エラーにしてくれますが、シグネチャを違えると暴走します。そんなsafeかunsafeかわからない言語Objective-C

typedかどうか、いやそれよりVMTから一発ジャンプなのかRTTIを検索するのかってのも重要な気がしますが、Delphiのdynamicメソッドのように静的型チェックされるのに実行時は検索なんて変り種もありますし、まあいろいろ直行してて例外もあって分類は難しいということで。あとModulaのGENERICとOCamlのfanctorは結局どうすればいいのかわかりませんでした。それとtemplateもオーバーロードコンパイル時多態という点で一緒になりかねないのが。ソースコードがsharedかどうかも必要かもしれません。
結論はとくに無いです。

関数ポインタ
ええと、動的で、非侵入で、呼び出し側が使い分けられるし……dynamic-nonintrusive-explicit(e/e/i)-multiple、と。おおこれ一番できること多いじゃないですか!