Хотелось бы понять причину такого поведения объектов OCAML. Предположим, у меня есть класс A
, который вызывает методы объекта другого класса B
. Схематически A#f вызывает B#g и B#h. Обычная практика в ООП заключается в том, что я хотел бы избежать использования B в качестве фиксированного конкретного класса, а вместо этого объявить только интерфейс для B. Как лучше всего сделать это в OCAML? Я попробовал несколько вариантов, и я не совсем понимаю, почему одни из них работают, а другие нет. Вот примеры кода.
Версия 1:
# class classA = object
method f b = b#g + b#h
end ;;
Error: Some type variables are unbound in this type:
class a : object method f : < g : int; h : int; .. > -> int end
The method f has type (< g : int; h : int; .. > as 'a) -> int where 'a
is unbound
Такое поведение хорошо известно: OCAML правильно делает вывод, что b
имеет тип открытого объекта
но затем жалуется, что мой класс не объявляет никаких переменных типа. Таким образом, кажется, что класс A
должен иметь переменные типа; Затем я явно ввел переменную типа.
Версия 2:
# class ['a] classA2 = object
method f (b:'a) = b#g + b#h
end ;;
class ['a] classA2 :
object constraint 'a = < g : int; h : int; .. > method f : 'a -> int end
Это работает, но класс теперь явно полиморфен с ограничением типа, как показывает OCAML. Также сбивает с толку то, что тип класса содержит переменную типа 'a
, и все же я могу сказать let x = new classA2
без указания значения типа для 'a
.Почему это возможно?
Другой недостаток classA2
заключается в том, что явное ограничение типа (b:'a)
содержит переменную типа. В конце концов, я знаю, что b
должен соответствовать фиксированному интерфейсу, а не неизвестному типу 'a
. Я хочу, чтобы OCAML проверил правильность этого интерфейса.
Итак, в версии 3 я сначала объявил интерфейс classB
как тип класса, а затем объявил, что b
должен быть этого типа:
# class type classB = object method g:int method h:int end;;
class type classB = object method g : int method h : int end
# class classA3 = object method f (b:classB) = b#g + b#h end;;
class classA3 : object method f : classB -> int end
Это тоже работает, но мое недоумение остается: почему classA3
больше не требует явного полиморфизма?
Резюме вопросов:
новый classA2
без указания типа для 'a
, хотя classA2
объявлен с переменная типа 'a
?classA3
принимает ограничение типа (b:classB)
и больше не требует связанной переменной типа?classA2
и classA3
каким-то тонким образом, и если да, то как?