osiire’s blog

ふしぎなそふとやさん

private row素晴らしすぎる

詳細はまた書くけど、「最低限こーゆーもの」を抽象化して輸入できる。素晴らしすぎる。

[追記]
例えば、二次元座標上の点を表現する何かを定義する。

class type ['a] point = object
  method x : 'a
  method y : 'a
end

このpoint型はgetterしか持ってないことに注意。値を変更することもできないし構築することもできない、実質座標を参照したいだけの型だ。
そして、このような参照だけ可能な型を使った点のリストを表現したいとする。まず思い付くのは#を使った次のような方法。

module RV = struct
  type 'a p = int #point as 'a
  type 'a t = 'a p list
  let make () = []
  let add (s : 'a p) t = s :: t
  let show (t : 'a t) =
    List.iter (fun s ->
      Printf.printf "x=%d,y=%d\n" s#x s#y) t
end

#pointは型変数を含むので、明示的に'aを付けてやる必要がある。これはこれでうまくいく。例えば、次のような点クラスを作ってRVに使える。

class ['a] z_point ~x ~y ~z = object
  val x : 'a = x with accessor
  val y : 'a = y with accessor
  val z : 'a = z with accessor
end
let _  =
  RV.show
  @@ RV.add (new z_point ~x:1 ~y:2 ~z:3) 
  @@ RV.add (new z_point ~x:4 ~y:5 ~z:6) 
  @@ RV.make ()

これでx=1,y=2とx=4,y=5が表示される。
ただ、type 'a pの定義には明示的な型変数が出てしまい、この型を利用する側としては有り難くない。そこでprivate row typeの登場。
module PVは次のように書ける。

module PV (X : sig type p = private int #point end) = struct
  type t = X.p list
  let make () = []
  let add s t : t = s :: t
  let show (t : t) =
    List.iter (fun s ->
      Printf.printf "x=%d,y=%d\n" s#x s#y) t
end

型変数の部分が型宣言内でprivate化されていて、表の型には型変数が必要ない。すばらしい。row多相なので、明示的なコアーションが要る訳でもない。ファクンター適用して使う。

let v1  =
  let module PV' = PV (struct type p = int z_point end) in
  PV'.show
  @@ PV'.add (new z_point ~x:1 ~y:2 ~z:3)
  @@ PV'.add (new z_point ~x:4 ~y:5 ~z:6) 
  @@ PV'.make ()