РЕДАКТИРОВАТЬ - Разделить ответ на 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;;
Возвращает:
Приглашенный продавец С. Боб
Приглашенный бухгалтер A Mary
Приглашенный продавец S Сью
Преимущество этого решения состоит в том, что сотрудникам не нужно знать о посетителе, и, следовательно, их можно определять в своих собственных единицах компиляции, что приводит к более чистому коду и сокращению перекомпиляции при добавлении новых типов сотрудников.