関数型のオブジェクトも命令型のオブジェクトも、どちらも複製することができます。ライブラリ関数の Oo.copy
はオブジェクトの「浅い」コピーを作ります。つまり、もとのオブジェクトと同じ内容のオブジェクトを返します。インスタンス変数はコピーされますが、その内容は共有されます。複製されたオブジェクトのインスタンス変数を(メソッド呼び出しを介して)変更しても、もとのオブジェクトのインスタンス変数は変化しません。逆の場合同じです。もちろん、より深いレベルでの代入(例えばインスタンス変数が参照セルであった場合)は、もとのオブジェクトと複製の両方に影響を与えます。
Oo.copy
の型は次のようになります。
#
Oo.copy;;
- : (< .. > as 'a) -> 'a = <fun>
型に現れる as
というキーワードは型変数 'a
をオブジェクトの型 < .. >
に束縛します。したがって、 Oo.copy
は任意のメソッド(..
で表されています)を持つオブジェクトを引数に取り、同じ型のオブジェクトを返すことになります。 Oo.copy
の型は < .. > -> < .. >
ではありません。なぜなら、二つの ..
は異るメソッドの組合わせを表すからです。 ..
は実際には型変数のように振舞います。
#
let p = new point 5;;
val p : point = <obj>
#
let q = Oo.copy p;;
val q : point = <obj>
#
q#move 7; (p#get_x, q#get_x);;
- : int * int = (5, 12)
p
のクラスで {< >}
を返すよう定義されたメソッド copy
を公開メソッドとして定義していたとすると、 Oo.copy p
は p#copy
と同じように振舞います。
オブジェクトは汎用比較関数 =
と <>
を使って比較することができます。ふたつのオブジェクトが等しいのは、オブジェクト同士が物理的に等しい場合です。特に、オブジェクトとその複製は等しくありません。
#
let q = Oo.copy p;;
val q : point = <obj>
#
p = q, p = p;;
- : bool * bool = (false, true)
<
、 <=
といった他の汎用の比較関数もオブジェクトに対して用いることができます。 <
の意味は規定されていませんが、全順序を与えることが保証されています。オブジェクト間の順序は、オブジェクトの生成時に決定され、インスタンス変数が変化しても順序が変化することはありません。
Oo.copy
と {< >}
は似た所があります。どちらも、オブジェクト内で、インスタンス変数を変えない自分自身のコピーを生成するのに使えます。
#
class copy = object method copy = {< >} end;;
class copy : object ('a) method copy : 'a end
#
class copy = object (self) method copy = Oo.copy self end;;
class copy : object ('a) method copy : 'a end
しかし、 {< ... >}
だけがインスタンス変数を書き換えられ、 Oo.copy
だけが、コピーしたいオブジェクトの外で使えます。
複製を使うとオブジェクトの状態を保存したり、過去の状態に復帰させたりする仕組みを提供することもできます。
#
class backup = object (self : 'mytype) val mutable copy = None method save = copy <- Some {< copy = None >} method restore = match copy with Some x -> x | None -> self end;;
class backup : object ('a) val mutable copy : 'a option method restore : 'a method save : unit end
上の例では、バックアップを一つだけ作ります。多重継承を用いて、どんなクラスにもバックアップ機能を付け加えることができます。
#
class ['a] backup_ref x = object inherit ['a] ref x inherit backup end;;
class ['a] backup_ref : 'a -> object ('b) val mutable copy : 'b option val mutable x : 'a method get : 'a method restore : 'b method save : unit method set : 'a -> unit end
#
let rec get p n = if n = 0 then p # get else get (p # restore) (n-1);;
val get : (< get : 'b; restore : 'a; .. > as 'a) -> int -> 'b = <fun>
#
let p = new backup_ref 0 in p # save; p # set 1; p # save; p # set 2; [get p 0; get p 1; get p 2; get p 3; get p 4];;
- : int list = [2; 1; 1; 1; 1]
すべてのコピーを保持するようなバックアップ機能を考えることもできます(すべてのバックアップを除去するメソッドを付け加えました)。
#
class backup = object (self : 'mytype) val mutable copy = None method save = copy <- Some {< >} method restore = match copy with Some x -> x | None -> self method clear = copy <- None end;;
class backup : object ('a) val mutable copy : 'a option method clear : unit method restore : 'a method save : unit end
#
class ['a] backup_ref x = object inherit ['a] ref x inherit backup end;;
class ['a] backup_ref : 'a -> object ('b) val mutable copy : 'b option val mutable x : 'a method clear : unit method get : 'a method restore : 'b method save : unit method set : 'a -> unit end
#
let p = new backup_ref 0 in p # save; p # set 1; p # save; p # set 2; [get p 0; get p 1; get p 2; get p 3; get p 4];;
- : int list = [2; 1; 0; 0; 0]