Шаблон дизайна посетителя в OCaml - PullRequest
4 голосов
/ 04 мая 2009

Я пытаюсь реализовать шаблон проектирования посетителя, используя OO-конструкции OCaml и систему типов, и сталкиваюсь с проблемами при создании экземпляра элемента.

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, поддерживая функциональность классов?

Редактировать Ошибка при вызове для полученияОценка:

This expression has type (<getName:string>, < getName:string>) lowerLevelHRRep 
but is here used with type <visitSalesman : 'a salesman -> unit > as 'a. 

Второй тип объекта не имеет метода visitAccountant.

1 Ответ

4 голосов
/ 04 мая 2009

РЕДАКТИРОВАТЬ - Разделить ответ на 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 Сью

Преимущество этого решения состоит в том, что сотрудникам не нужно знать о посетителе, и, следовательно, их можно определять в своих собственных единицах компиляции, что приводит к более чистому коду и сокращению перекомпиляции при добавлении новых типов сотрудников.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...