#ocamlbook#
大堀先生の論文他を超斜め読みした理解はこんな感じなのですがどうでしょう.
# let c = object val lst = [] method cons x = {< lst = (x::lst) >} method length = List.length lst end;; val c : < cons : 'b -> 'a; length : int > as 'a = <obj> # let s = object val lst = [] method snoc x = {< lst = (List.append lst [x]) >} method length = List.length lst end;; val s : < length : int; snoc : 'b -> 'a > as 'a = <obj>
上記のようなintを返すメソッドlengthを持つという共通点を持つオブジェクトc,sがあったとし,
# let tupler o = (o#length,o);; val tupler : (< length : 'b; .. > as 'a) -> 'b * 'a = <fun>
上記のようにメソッドlengthを持つオブジェクトをいじる関数を定義.
tuplerの型中の..をとりあえず,無視して読むと,
lenthを持つ何か -> (何か,lengthを持つ何か)
というシグネチャで,戻り値の2要素目はlengthを持つという情報以外を失っているように見える.
しかし,
# (snd (tupler c))#cons 1;; - : < cons : int -> 'a; length : int > as 'a = <obj> # (snd (tupler c))#snoc 1;; This expression has type < cons : 'b -> 'a; length : int > as 'a It has no method snoc
ちゃんと,cから作った(snd (tupler c))はconsを持っているものという情報をロスしておらず,
# (snd (tupler s))#cons 1;; This expression has type < length : int; snoc : 'b -> 'a > as 'a It has no method cons # (snd (tupler s ))#snoc 1;; - : < length : int; snoc : int -> 'a > as 'a = <obj>
sに関して同様の操作を行ったこの例からわかるように,
先ほどは(snd (tupler c))にconsを先に適用した結果,
consを持つものとされたのでなく,
tuplerに渡されたオブジェクトの情報が隠れた形で保存されている.
この隠れた情報保持している裏方さんがrow variable(rho variable) とやらで,
こういったpolymorphicなものが記述できてハッピーだ.
というのが,ρ多相という認識でOKでしょうか?
すばらしい、数時間で完璧に理解されていますね。私がρ多相を理解するのに何日かかったと思っているんですかっ あと、実用上は、情報のロスを防げるだけではなく、明示的なサブタイピングのように キャストを記述しなくてもいいという点も、便利なところだと思っています。 上記の例でそのまま5章の内容にできますね。よろしくお願いします。
ところで、row variablesについて理解できると、実は、特定のコンストラクタだけ ファンクターの引数にして輸入できるprivate rowsの機能も容易に理解できます。 ついでに理解しておくと役立つと思います。