我正在尝试使用OCaml的OO结构和类型系统来实现访问者设计模式,并在实例化元素时遇到问题。
class virtual ['hrRep] employee = object
method virtual receiveEvaluation : 'hrRep -> unit
method virtual getName : string
end;;
class ['hrRep] accountant myName = object (self : 'a)
inherit ['hrRep]employee
val name = myName
method receiveEvaluation rep = rep#visitAccountant self
method getName = name
end;;
class ['hrRep] salesman myName = object (self : 'a)
inherit ['hrRep]employee
val name = myName
method receiveEvaluation rep = rep#visitSalesman self
method getName = name
end;;
class virtual ['accountant, 'salesman] hrRep = object (self)
method virtual visitSalesman : 'salesman -> unit
method virtual visitAccountant : 'accountant -> unit
end;;
class ['employee, 'salesman] lowerLevelHRRep =
object (self) inherit ['employee, 'salesman]hrRep
method visitSalesman s = print_endline ("Visiting salesman "^s#getName)
method visitAccountant a =
print_endline ("Visiting accountant "^a#getName)
end;;
let s1 : (<visitSalesman : 'a -> unit>) salesman = new salesman "Bob";;
let a1 : (<visitAccountant : 'a -> unit>) accountant = new accountant "Mary";;
let s2 : (<visitSalesman : 'a -> unit>) salesman = new salesman "Sue";;
let h1 : (<getName : string>, <getName : string>) lowerLevelHRRep = new lowerLevelHRRep;;
s1#receiveEvaluation h1;;
我在编译时遇到的错误是:
The type of this expression, <visitSalesman : 'a -> unit; _.. > salesman as 'a,
contains type variables that cannot be generalized.
但是,代码编译减去实例化salesman
的行。
如何在保持类功能的同时实例化salesman
?
修改通过调用receiveEvaluation收到错误:
This expression has type (<getName:string>, < getName:string>) lowerLevelHRRep
but is here used with type <visitSalesman : 'a salesman -> unit > as 'a.
第二个对象类型没有方法visitAccountant
。
答案 0 :(得分:4)
编辑 - 将答案分为3个要点:初始编译错误的解决方案,递归解决方案和参数化解决方案
解决编译错误
请注意,您的代码在顶级版中运行良好:
# let s = new salesman ();;
val s : < visitSalesman : 'a -> unit; _.. > salesman as 'a = <obj>
这种编译错误通常通过添加类型注释来帮助编译器确定类型来解决。由于顶层告诉我们它是什么,我们可以修改实例化:
let s : (< visitSalesman : 'a -> unit>) salesman = new salesman ();;
这个编译!
递归解决方案
通过使用递归类可以降低复杂性。这完全不需要参数化类,但意味着需要在同一个源文件中定义所有对象。
class virtual employee =
object
method virtual receiveEvaluation:(hrrep -> unit)
end
and accountant =
object(self)
inherit employee
method receiveEvaluation:(hrrep -> unit) = fun rep -> rep#visitAccountant (self :> accountant)
end
and salesman =
object (self)
inherit employee
method receiveEvaluation:(hrrep -> unit) = fun rep -> rep#visitSalesman (self :> salesman)
end
and hrrep =
object
method visitSalesman:(salesman -> unit) = fun s -> print_endline ("Visiting salesman")
method visitAccountant:(accountant -> unit) = fun s -> print_endline ("Visiting accountant")
end
let s = new salesman;;
let e = (s :> employee);;
let v = new hrrep;;
e#receiveEvaluation v;;
打印“访问推销员”。对员工的强制只是为了使其更接近现实世界的情景。
参数化解决方案
再次查看问题,我认为没有必要使用参数化的hrRep,因为此时所有其他类型都是已知的。通过简化员工类的参数化,我得到了这个:
class virtual ['a] employee =
object
method virtual receiveEvaluation : 'a -> unit
method virtual getName : string
end
class ['a] accountant name =
object(self)
inherit ['a] employee
val name = name
method receiveEvaluation rep = rep#visitAccountant self
method getName = "A " ^ name
end
class ['a] salesman name =
object(self)
inherit ['a] employee
val name = name
method receiveEvaluation rep = rep#visitSalesman self
method getName = "S " ^ name
end
class virtual hrRep =
object
method virtual visitAccountant : hrRep accountant -> unit
method virtual visitSalesman : hrRep salesman -> unit
end
class lowerLevelHRRep =
object
inherit hrRep
method visitAccountant a = print_endline ("Visiting accountant " ^ a#getName)
method visitSalesman s = print_endline ("Visiting salesman " ^ s#getName)
end;;
let bob = new salesman "Bob";;
let mary = new accountant "Mary";;
let sue = new salesman "Sue";;
let h = new lowerLevelHRRep;;
bob#receiveEvaluation h;;
mary#receiveEvaluation h;;
sue#receiveEvaluation h;;
返回:
拜访推销员S Bob
访问会计师A Mary
拜访推销员S Sue
此解决方案的优点是员工无需了解访问者,因此可以在他们自己的编译单元中进行定义,从而在添加新类型的员工时可以实现更清晰的代码和更少的重新编译。