そのオブジェクト自体と同じ型の引数を取るメソッドのことをバイナリメソッドと言います。下の 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