Как правильно создать подкласс Ocaml с дополнительными методами? - PullRequest
8 голосов
/ 06 февраля 2011

В Ocaml я борюсь с подклассами и типами:

class super =  
  object (self)  
  method doIt =   
    ...  
end;  

class sub =  
  object (self)  
  inherit super  
  method doIt =   
    ...  
    self#somethingElse  
    ...  

  method somethingElse =  
    ...  
end;  

let myFunction (s:super) =  
  ...  

myFunction new sub

Очевидно, в Ocaml класс sub равен , а не"подтип" класса super, потому чтометод sub#doIt вызывает метод в sub, которого нет в super.Тем не менее, это кажется довольно распространенным вариантом использования для программирования ОО.Каков рекомендуемый способ сделать это?

Ответы [ 3 ]

9 голосов
/ 07 февраля 2011

Как упомянул Реми, проблема с вашим кодом заключается в том, что система типов OCaml поддерживает только один тип на выражение: выражение типа sub не относится к типу super.В вашем примере myFunction ожидает аргумент типа super, а выражение new sub имеет тип sub, поэтому возникает проблема.

Обновление имеет важное значение для объектно-ориентированного программирования, и OCamlдействительно поддерживает это с двумя отличными конструкциями.

Первым является принуждение типа .Если super является супертипом sub (это означает, что семантически значения типа sub ведут себя как значения super) и x : sub, то (x :> super) : super.Оператор типа :> делает преобразование явным - эквивалент того, что неявно делают популярные объектно-ориентированные языки, когда вы используете значение типа sub, где ожидается super.

Второй - ограничения супертипа : требование, чтобы переменная данного типа была подтипом данного типа.Это записывается как #super или (#super as 'a), если вы хотите назвать переменную типа внутри.Ограничения супертипа на самом деле не изменяют тип выражения, как приведения типов, они просто проверяют, является ли тип допустимым подтипом требуемого типа.

Чтобы лучше понять разницу, рассмотрим следующий пример:

class with_size ~size = object 
  val size    = size : int
  method size = size
end

class person ~name ~size = object
  inherit with_size ~size
  val name    = name : string
  method name = name
end

let pick_smallest_coerce (a : with_size) (b : with_size) = 
  if a # size < b # size then a else b

let pick_smallest_subtype (a : #with_size) (b : #with_size) = 
  if a # size < b # size then a else b

Тип pic_smallest_coerce равен with_size -> with_size -> with_size: даже если вы передали два экземпляра person,возвращаемое значение будет иметь тип with_size, и вы не сможете вызвать его name метод.

Тип pic_smallest_subtype - (#with_size as 'a) -> 'a -> 'a: если вы передадите два экземпляра person, система типов определит это 'a = person и правильно определит возвращаемое значение как тип person (которыйпозволяет использовать метод name).

Короче говоря, ограничения супертипа просто гарантируют, что ваш код будет работать, без потери какой-либо информации о типе - переменная сохраняет свой первоначальный тип,Принуждение к типу фактически теряет информацию о типе (что при отсутствии приведения является очень неприятной вещью), поэтому ее следует использовать только в качестве крайней меры в двух ситуациях:

1. Вы не можете иметь полиморфную функцию.Ограничения супертипа основаны на том, что #super является переменной свободного типа, поэтому, если вы не можете позволить себе иметь переменную свободного типа в вашем коде, вам придется обходиться без нее.

2. Вам необходимо хранить значения различных фактических типов в одном контейнере.Список или ссылка, которые могут содержать либо person, либо box экземпляров, будут использовать with_size и приведение:

let things = [ my_person :> with_size ; my_box :> with_size ]

Обратите внимание, что алгоритм вывода типов самостоятельно обнаружит ограничения супертипа (он будетне определять, какой класс или тип класса вы намеревались использовать, но он будет создавать буквальный тип класса):

let pick_smallest_infer a b = 
  if a # size < b # size then a else b

val pick_smallest_infer : (< size : 'a ; .. > as 'b) -> 'b -> 'b

Таким образом, за редкими исключениями, аннотирование фактических ограничений супертипа является полезным упражнением только при документированиитвой код.

8 голосов
/ 06 февраля 2011

sub, вероятно, является подтипом super.Но в ocaml нет неявного преобразования типов.Таким образом, ваша функция не принимает подтип супер.Вы должны явно сделать принуждение:

let myFunction (s:super) =  
  ...  

myFunction (new sub :> super)

Или предпочтительно принять подтип super:

let myFunction (s:#super) =  
  ...  

myFunction new sub
0 голосов
/ 07 февраля 2011

Если вы хотите, чтобы myFunction принял любой подтип super в качестве аргумента, вы должны определить его следующим образом:

let myFunction s =
  let s = (s :> super) in
  ...

... или эквивалентно ...

let myFunction (s :> super) =
  ...

Что касается вашего комментария, то sub в вашем примере не является подтипом super, потому что метод doIt в sub - это то, что отправляется, когда значение объекта слева от # оператор типа sub, я думаю, вы ошибаетесь. Это именно то, что вы должны ожидать.

Чтобы методы в классе sub могли вызывать метод doIt в классе super, вы должны определить их следующим образом:

class super = object (self)  
  method doIt =   
    ...  
end;  

class sub = object (self)  
  inherit super as parent

  method doIt =   
    ...
    let _ = parent#doIt in
    ...
    self#somethingElse  
    ...  

  method somethingElse =  
    ...  
end;
...