Давайте посмотрим на сигнатуру некоторых задействованных модулей.Это сигнатуры, сгенерированные Ocaml, и они являются основными сигнатурами, т.е. они являются наиболее общими сигнатурами, допускаемыми теорией.
module Make : functor (Arg : TYPE) -> sig
type t = Arg.t
val default : t
val get : t option -> t
end
module Made : sig
type t
val default : t
val get : t option -> t
end
Обратите внимание, как сохраняется уравнение Make(A).t = A.t
(поэтому Make(A).t
является прозрачным сокращением типа), но Made.t
является абстрактным.Это связано с тем, что Made
является результатом применения функтора к анонимной структуре, поэтому в данном случае нет канонического имени для типа аргумента.
Типы записей являются порождающими.На уровне базовой теории типов все порождающие типы ведут себя как абстрактные типы с некоторым синтаксическим сахаром для конструкторов и деструкторов.Единственный способ обозначить порождающий тип - дать его имя, либо оригинальное имя, либо расширение, которое расширяется до исходного имени с помощью ряда уравнений типа.
Подумайте, что произойдет, если вы продублируете определение Made
:
module Made1 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
module Made2 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
Вы получаете два разных типа Made1.t
и Made2.t
, хотя правые части определений одинаковы.Вот что такое генеративность.
Поскольку Made.t
является абстрактным, это не тип записи.У него нет конструктора.Конструкторы были потеряны, когда аргумент структуры был закрыт из-за отсутствия имени.
Бывает, что с записями часто требуется синтаксический сахар, но не порождаемость.Но у Окамла нет структурных типов записей.У него есть генеративные типы записей, и у него есть объекты, которые с теоретической точки зрения типа включают записи, но на практике может потребоваться немного больше усилий для использования и небольшого снижения производительности.
module Made_object = Make(struct
type t = <a : int>
let default = object method a = 0 end
end)
Или, если выЕсли вы хотите сохранить одно и то же определение типа, вам нужно предоставить имя для типа и его конструкторов, что означает присвоение имени структуре.
module A = struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end
module MadeA = Make(A)
Обратите внимание, что если вы дважды создаете Make(A)
, вы получаете то же самоетипы вокруг.
module MadeA1 = Make(A)
module MadeA2 = Make(A)
(Хорошо, здесь это не замечательно, но вы все равно получите те же абстрактные типы в MadeA1
и MakeA2
, в отличие отMade1
и Made2
случай выше. Это потому, что теперь есть имя для этих типов: MadeA1.t = Make(A).t
.)