そのオブジェクト自体と同じ型の引数を取るメソッドのことをバイナリメソッドと言います。下の comparable
クラスは型 'a -> bool
なるバイナリメソッド leq
を持つクラスのテンプレートです。ここで 'a
はオブジェクト自体の型に束縛されています。したがって、 #comparable
は < leq : 'a -> bool; .. > as 'a
の略記となります。ここで as
が再帰的な型を表記するのに使えることが分ります。
#
class virtual comparable = object (_ : 'a) method virtual leq : 'a -> bool end;;
class virtual comparable : object ('a) method virtual leq : 'a -> bool end
さて、 comparable
の子クラス money
を定義しましょう。 money
クラスは、単に浮動小数点数型を comparable
オブジェクトになるようにしたものです。後で、もっと操作を加えることにします。 <=
は Objective Caml では多相型を持つので、クラス引数 x
には型制約が与えられています。 inherit
節によってこのクラスのオブジェクトが #comparable
のインスタンスであることが保証されています。
#
class money (x : float) = object inherit comparable val repr = x method value = repr method leq p = repr <= p value end;;
class money : float -> object ('a) val repr : float method leq : 'a -> bool method value : float end
型 money
は comparable
の部分型でないことに注意して下さい。というのは、自分自身の型が引数の位置に現れているからです。実際、クラス money
のオブジェクト m
のメソッド leq
は引数の value
メソッドを呼び出します。もし、m
が型 comparable
を持つとみなせたとすると、 value
メソッドを持たないオブジェクトを引数として m
の leq
メソッドを呼び出せることになり、エラーとなります。
同じように、次の型 money2
は money
の部分型ではありません。
#
class money2 x = object inherit money x method times k = {< repr = k *. repr >} end;;
class money2 : float -> object ('a) val repr : float method leq : 'a -> bool method times : float -> 'a method value : float end
しかし、 money
と money2
のいずれの型を持つオブジェクトでも機能する関数を定義することができます。関数 min
は #comparable
に単一化する型を持つ二つのオブジェクトのうち、最小のものを返します。 min
の型は #comparable -> #comparable -> #comparable
ではありません。というのは、#comparable
は型変数 (..
の部分)を隠蔽するからです。これでは、それぞれの #comparable
が新しい型変数を導入してしまいます。
#
let min (x : #comparable) y = if x#leq y then x else y;;
val min : (#comparable as 'a) -> 'a -> 'a = <fun>
この関数は money
と money2
のどちらの型をもつオブジェクトにも適用できます。
#
(min (new money 1.3) (new money 3.1))#value;;
- : float = 1.3
#
(min (new money2 5.0) (new money2 3.14))#value;;
- : float = 3.14
バイナリメソッドの他の例は 5.2.1 節「文字列 」と 5.2.4 節「集合 」にあげられています。
times
メソッドで {< ... >}
構文を用いていることに注意して下さい。 {< repr = k *. repr >}
の代わりに new money2 (k *. repr)
と書くと、継承をしたときにうまく動きません。すなわち、 money2
の子クラス money3
で times
メソッドが期待した money3
クラスではなく money2
クラスのオブジェクトを返すようになってしまうのです。
money
クラスには当然他のバイナリメソッドあるでしょう。ここでは直接定義します。
#
class money x = object (self : 'a) val repr = x method value = repr method print = print_float repr method times k = {< repr = k *. x >} method leq (p : 'a) = repr <= p value method plus (p : 'a) = {< repr = x +. p value >} end;;
class money : float -> object ('a) val repr : float method leq : 'a -> bool method plus : 'a -> 'a method print : unit method times : float -> 'a method value : float end