Таким образом, IdUser
на самом деле является экзистенциальным типом: для User
существует тип
IdUser.current
такой, что общественность IdUser.t
может быть поднята к ней. Есть несколько способов закодировать это: либо functorize User
, как показывает Гаше, если статического управления зависимостями достаточно, или используйте первоклассные модули или объекты, если вам нужно больше динамизма.
Я еще немного разберу пример Гаше, используя сокращения частного типа для удобства и покажу, как использовать полупрозрачность, чтобы избежать слишком частой приватизации типов реализации. Во-первых, и это может быть ограничением, я хочу объявить ADT постоянного IDs
:
(* File id.ml *)
module type ID = sig
type t
type current = private t
end
module type PERSISTENT_ID = sig
include ID
val persist : t -> current
end
С помощью этого я могу определить тип Post
s, используя конкретные типы для ID
s, но с помощью ADT для обеспечения соблюдения бизнес-правил, касающихся персистентности:
(* File post.ml *)
module Post
(UID : ID with type t = string)
(PID : PERSISTENT_ID with type t = int)
: sig
val is_mine : UID.current -> PID.t -> PID.current
end = struct
let is_mine uid pid =
if (uid : UID.current :> UID.t) = "me" && pid = 0
then PID.persist pid
else failwith "is_mine"
end
То же самое с User
s:
(* File user.ml *)
module User
(UID : PERSISTENT_ID with type t = string)
: sig
val check_password : UID.t -> password:string -> UID.current
end = struct
let check_password uid ~password =
if uid = "scott" && password = "tiger"
then UID.persist uid
else failwith "check_password"
end
Обратите внимание, что в обоих случаях я использую конкретные, но частные ID
типы. Связать все вместе - это просто определить ID
ADT с их правилами постоянства:
module IdUser = struct
type t = string
type current = string
let persist x = x
end
module IdPost = struct
type t = int
type current = int
let persist x = x
end
module MyUser = User (IdUser)
module MyPost = Post (IdUser) (IdPost)
На этом этапе и для полного разделения зависимостей вам, вероятно, понадобятся подписи для USER
и POST
, которые можно экспортировать из этого модуля, но их просто добавить.