val neg : < times : float -> 'a; .. > -> 'a = <fun>
#class account = objectvalmutable balance = zero method balance = balance method deposit x = balance <- balance # plus x method withdraw x = if x#leq balance then (balance <- balance # plus (neg x); x) else zero end;;
class account : objectvalmutable balance : Euro.c method balance : Euro.c method deposit : Euro.c -> unit method withdraw : Euro.c -> Euro.c end
#let c = new account in c # deposit (euro 100.); c # withdraw (euro 50.);;
#class safe_account = objectinherit account method deposit x = if zero#leq x then balance <- balance#plus x end;;
class safe_account : objectvalmutable balance : Euro.c method balance : Euro.c method deposit : Euro.c -> unit method withdraw : Euro.c -> Euro.c end
但是,可以通过以下定义更安全地修复该错误
#class safe_account = objectinherit account as unsafe method deposit x = if zero#leq x then unsafe # deposit x else raise (Invalid_argument "deposit") end;;
class safe_account : objectvalmutable balance : Euro.c method balance : Euro.c method deposit : Euro.c -> unit method withdraw : Euro.c -> Euro.c end
特别是,这不需要了解方法 deposit 的实现。
为了跟踪操作,我们使用可变字段 history 和私有方法 trace 扩展类,以在日志中添加操作。然后重新定义每个要跟踪的方法。
#type 'a operation = Deposit of 'a | Retrieval of 'a;;
type 'a operation = Deposit of 'a | Retrieval of 'a
#class account_with_history = object (self) inherit safe_account as super valmutable history = [] methodprivate trace x = history <- x :: history method deposit x = self#trace (Deposit x); super#deposit x method withdraw x = self#trace (Retrieval x); super#withdraw x method history = List.rev history end;;
class account_with_history : objectvalmutable balance : Euro.c valmutable history : Euro.c operation list method balance : Euro.c method deposit : Euro.c -> unit method history : Euro.c operation list methodprivate trace : Euro.c operation -> unit method withdraw : Euro.c -> Euro.c end
人们可能希望开户并同时存入一些初始金额。虽然初始实现没有解决此要求,但可以通过使用初始化器来实现。
#class account_with_deposit x = objectinherit account_with_history initializer balance <- x end;;
class account_with_deposit : Euro.c -> objectvalmutable balance : Euro.c valmutable history : Euro.c operation list method balance : Euro.c method deposit : Euro.c -> unit method history : Euro.c operation list methodprivate trace : Euro.c operation -> unit method withdraw : Euro.c -> Euro.c end
更好的选择是
#class account_with_deposit x = object (self) inherit account_with_history initializer self#deposit x end;;
class account_with_deposit : Euro.c -> objectvalmutable balance : Euro.c valmutable history : Euro.c operation list method balance : Euro.c method deposit : Euro.c -> unit method history : Euro.c operation list methodprivate trace : Euro.c operation -> unit method withdraw : Euro.c -> Euro.c end
实际上,后者更安全,因为对 deposit 的调用将自动受益于安全检查和跟踪。让我们测试一下
#let ccp = new account_with_deposit (euro 100.) inlet _balance = ccp#withdraw (euro 50.) in ccp#history;;
- : Euro.c operation list = [Deposit <obj>; Retrieval <obj>]
关闭账户可以使用以下多态函数完成
#let close c = c#withdraw c#balance;;
val close : < balance : 'a; withdraw : 'a -> 'b; .. > -> 'b = <fun>
当然,这适用于各种类型的账户。
最后,我们将账户的多个版本收集到一个模块 Account 中,该模块抽象化为某种货币。
#let today () = (01,01,2000) (* 近似值 *)module Account (M:MONEY) = structtype m = M.c let m = new M.c let zero = m 0. class bank = object (self) valmutable balance = zero method balance = balance valmutable history = [] methodprivate trace x = history <- x::history method deposit x = self#trace (Deposit x); if zero#leq x then balance <- balance # plus x else raise (Invalid_argument "deposit") method withdraw x = if x#leq balance then (balance <- balance # plus (neg x); self#trace (Retrieval x); x) else zero method history = List.rev history endclasstype client_view = objectmethod deposit : m -> unit method history : m operation list method withdraw : m -> m method balance : m endclassvirtual check_client x = let y = if (m 100.)#leq x then x else raise (Failure "Insufficient initial deposit") inobject (self) initializer self#deposit y methodvirtual deposit: m -> unit endmodule Client (B : sigclass bank : client_view end) = structclass account x : client_view = objectinherit B.bank inherit check_client x endlet discount x = let c = new account x inif today() < (1998,10,30) then c # deposit (m 100.); c endend;;
#module Investment_account (M : MONEY) = structtype m = M.c module A = Account(M) class bank = objectinherit A.bank as super method deposit x = if (new M.c 1000.)#leq x then print_string "Would you like to invest?"; super#deposit x endmodule Client = A.Client end;;
当账户的一些新功能可以提供给客户时,也可以重新定义函子Client。
#module Internet_account (M : MONEY) = structtype m = M.c module A = Account(M) class bank = objectinherit A.bank method mail s = print_string s endclasstype client_view = objectmethod deposit : m -> unit method history : m operation list method withdraw : m -> m method balance : m method mail : string -> unit endmodule Client (B : sigclass bank : client_view end) = structclass account x : client_view = objectinherit B.bank inherit A.check_client x endendend;;
#class better_string s = objectval repr = s method get n = String.get repr n method print = print_string repr method escaped = {< repr = String.escaped repr >} method sub start len = {< repr = String.sub s start len >} end;;
class better_string : string -> object ('a) val repr : string method escaped : 'a method get : int -> char method print : unit method sub : int -> int -> 'a end
#class ostring s = object (self : 'mytype) val repr = s method repr = repr method get n = String.get repr n method print = print_string repr method escaped = {< repr = String.escaped repr >} method sub start len = {< repr = String.sub s start len >} method concat (t : 'mytype) = {< repr = repr ^ t#repr >} end;;
class ostring : string -> object ('a) val repr : string method concat : 'a -> 'a method escaped : 'a method get : int -> char method print : unit method repr : string method sub : int -> int -> 'a end
#class ['a] stack = objectvalmutable l = ([] : 'a list) method push x = l <- x::l method pop = match l with [] -> raise Empty | a::l' -> l <- l'; a method clear = l <- [] method length = List.length l end;;
class ['a] stack : objectvalmutable l : 'a list method clear : unit method length : int method pop : 'a method push : 'a -> unit end
然而,编写一个用于遍历栈的方法则比较棘手。一个名为 fold 的方法将具有类型 ('b -> 'a -> 'b) -> 'b -> 'b。这里 'a 是栈的参数。参数 'b 与类 'a stack 无关,而是与将传递给 fold 方法的参数有关。一个简单的做法是将 'b 作为类 stack 的额外参数。
#class ['a, 'b] stack2 = objectinherit ['a] stack method fold f (x : 'b) = List.fold_left f x l end;;
class ['a, 'b] stack2 : objectvalmutable l : 'a list method clear : unit method fold : ('b -> 'a -> 'b) -> 'b -> 'b method length : int method pop : 'a method push : 'a -> unit end
#class ['a] stack3 = objectinherit ['a] stack method fold : 'b. ('b -> 'a -> 'b) -> 'b -> 'b = fun f x -> List.fold_left f x l end;;
class ['a] stack3 : objectvalmutable l : 'a list method clear : unit method fold : ('b -> 'a -> 'b) -> 'b -> 'b method length : int method pop : 'a method push : 'a -> unit end
这是第 3.17 节中看到的友元函数的另一个实例。实际上,这是在没有对象的情况下模块 Set 中使用的相同机制。
在集合的面向对象版本中,我们只需要添加一个额外的 tag 方法来返回集合的表示。由于集合在元素类型上是参数化的,因此 tag 方法具有参数化类型 'a tag,在模块定义中是具体的,但在其签名中是抽象的。从外部来看,它将保证具有相同类型的 tag 方法的两个对象将共享相同的表示。
#moduletype SET = sigtype 'a tag class ['a] c : object ('b) method is_empty : bool method mem : 'a -> bool method add : 'a -> 'b method union : 'b -> 'b method iter : ('a -> unit) -> unit method tag : 'a tag endend;;
#module Set : SET = structletrec merge l1 l2 = match l1 with [] -> l2 | h1 :: t1 -> match l2 with [] -> l1 | h2 :: t2 -> if h1 < h2 then h1 :: merge t1 l2 elseif h1 > h2 then h2 :: merge l1 t2 else merge t1 t2 type 'a tag = 'a list class ['a] c = object (_ : 'b) val repr = ([] : 'a list) method is_empty = (repr = []) method mem x = List.exists (( = ) x) repr method add x = {< repr = merge [x] repr >} method union (s : 'b) = {< repr = merge repr s#tag >} method iter (f : 'a -> unit) = List.iter f repr method tag = repr endend;;
class ['a, 'event] subject : object ('b) constraint 'a = < notify : 'b -> 'event -> unit; .. > valmutable observers : 'a list method add_observer : 'a -> unit method notify_observers : 'event -> unit end
#class ['observer] window_subject = let id = count := succ !count; !count inobject (self) inherit ['observer, event] subject valmutable position = 0 method identity = id method move x = position <- position + x; self#notify_observers Move method draw = Printf.printf "{Position = %d}\n" position; end;;
class ['a] window_subject : object ('b) constraint 'a = < notify : 'b -> event -> unit; .. > valmutable observers : 'a list valmutable position : int method add_observer : 'a -> unit method draw : unit method identity : int method move : int -> unit method notify_observers : event -> unit end
#class ['subject] window_observer = objectinherit ['subject, event] observer method notify s e = s#draw end;;
class ['a] window_observer : objectconstraint 'a = < draw : unit; .. > method notify : 'a -> event -> unit end
正如预期的那样,window 的类型是递归的。
#let window = new window_subject;;
val window : (< notify : 'a -> event -> unit; .. > as '_weak3) window_subject as 'a = <obj>
但是,window_subject 和 window_observer 这两个类不是相互递归的。
#let window_observer = new window_observer;;
val window_observer : (< draw : unit; .. > as '_weak4) window_observer = <obj>
class ['a] richer_window_subject : object ('b) constraint 'a = < notify : 'b -> event -> unit; .. > valmutable observers : 'a list valmutable position : int valmutable size : int valmutable top : bool method add_observer : 'a -> unit method draw : unit method identity : int method move : int -> unit method notify_observers : event -> unit method raise : unit method resize : int -> unit end
#class ['subject] richer_window_observer = objectinherit ['subject] window_observer as super method notify s e = if e <> Raise then s#raise; super#notify s e end;;
class ['a] richer_window_observer : objectconstraint 'a = < draw : unit; raise : unit; .. > method notify : 'a -> event -> unit end