参照セルはオブジェクトとして実装できます。しかし、素朴な定義は型チェックを通過できません。
#
class ref x_init = object val mutable x = x_init method get = x method set y = x <- y end;;
Some type variables are unbound in this type: class ref : 'a -> object val mutable x : 'a method get : 'a method set : 'a -> unit end The method get has type 'a where 'a is unbound
その理由は、セルに保存された値の型が指定されていないためメソッドの一つが多相型であるためです。したがって、保存される値の型をクラスがパラメータとして持つか、クラスの型が単相型に束縛されなければなりません。単相型として定義するには次のようにします。
#
class ref (x_init:int) = object val mutable x = x_init method get = x method set y = x <- y end;;
class ref : int -> object val mutable x : int method get : int method set : int -> unit end
直接生成されたオブジェクトはクラス型を定義しないので、この制限はありません。
#
let new_ref x_init = object val mutable x = x_init method get = x method set y = x <- y end;;
val new_ref : 'a -> < get : 'a; set : 'a -> unit > = <fun>
一方、多相的な参照のクラス定義では、その型変数を明示する必要があります。クラスの型変数は常に [
と ]
の間に列挙しなくてはいけません。この型パラメータはクラス本体のどこかで型制約によって束縛されていなければなりません。
#
class ['a] ref x_init = object val mutable x = (x_init : 'a) method get = x method set y = x <- y end;;
class ['a] ref : 'a -> object val mutable x : 'a method get : 'a method set : 'a -> unit end
#
let r = new ref 1 in r#set 2; (r#get);;
- : int = 2
宣言中の型パラメータが実際にクラス定義の本体で制約を受けるかもしれません。クラス型中では、型パラメータの実際の値は constraint
節で表示されます。
#
class ['a] ref_succ (x_init:'a) = object val mutable x = x_init + 1 method get = x method set y = x <- y end;;
class ['a] ref_succ : 'a -> object constraint 'a = int val mutable x : int method get : int method set : int -> unit end
もっと複雑な例を考えましょう。「点」オブジェクトと、その任意のサブオブジェクトを中心とする「円」を定義することを考えましょう。 move
メソッドには型制約を追加しなければなりません。というのは、クラス型の型パラメータでは束縛されない自由変数があってはならないからです。
#
class ['a] circle (c : 'a) = object val mutable center = c method center = center method set_center c = center <- c method move = (center move : int -> unit) end;;
class ['a] circle : 'a -> object constraint 'a = < move : int -> unit; .. > val mutable center : 'a method center : 'a method move : int -> unit method set_center : 'a -> unit end
他にも、以下に示すように constraint
節を用いて circle
クラスを定義することもできます。 constraint
節で用いられている #point
という型は、 point
クラスを定義する時に自動的に定義されます。この型は point
クラスの任意の子クラスに属するオブジェクトと単一化します。 #point
は実際には < get_x : int; move : int -> unit; .. >
という型の略記です。この表記を用いると、次のような circle
クラスの定義を得ることができます。このクラス定義は、引数に対して若干以前よりも強い制約を科します。というのは center
が get_x
メソッドを持つことを求めているからです。
#
class ['a] circle (c : 'a) = object constraint 'a = point val mutable center = c method center = center method set_center c = center <- c method move = center move end;;
class ['a] circle : 'a -> object constraint 'a = #point val mutable center : 'a method center : 'a method move : int -> unit method set_center : 'a -> unit end
colored_circle
クラスは circle
クラスの特殊化したもので、「中心」の型が #colored_point
に単一化されることを求めます。また、 color
メソッドを持っています。パラメータ付きクラスを継承する場合、型変数を再び明示する必要があります。型変数は、以前と同様に [
と ]
の間に書きます。
#
class ['a] colored_circle c = object constraint 'a = colored_point inherit ['a] circle c method color = center color end;;
class ['a] colored_circle : 'a -> object constraint 'a = #colored_point val mutable center : 'a method center : 'a method color : string method move : int -> unit method set_center : 'a -> unit end