OCaml中的朋友模块

Vic*_*let 5 dependencies ocaml module friend

我目前有两个"层"模块,代表数据库中的标识符 - 数据关系.

所述第一层限定标识符类型,例如IdUser.tIdPost.t,而第二层定义了数据类型,例如User.tPost.t.我需要在第二层的模块之前编译第一层的所有模块,因为Post.t必须保留IdUser.t其作者并User.t保留IdPost.t他访问的最后五个帖子.

现在,IdUser.t提供只应该被使用的功能User.t,例如将变换IdUser.tIdUser.current:的能力.出于安全原因,此变换必须仅由函数执行User.check_password.由于IdUserUser是独立的模块,我需要定义这些功能作为公共职能,依靠约定来避免调用它们以外的任何位置User,这是相当脏.对称情况发生在IdPost.mine:

module IdUser : sig
  type t
  type current
  val current_of_t : t -> current (* <--- Should not be public! *)
end = struct 
  type t = string
  type current = string
  let current_of_t x = x
end

module IdPost : sig
  type t
  type mine
  val mine_of_t   : t -> mine (* <--- Should not be public! *)
end = struct 
  type t = string
  type mine = string
  let mine_of_t   x = x
end

module Post : sig 
 (* Should not "see" IdUser.current_of_t but needs IdPost.mine_of_t *)
  val is_mine : IdUser.current -> IdPost.t -> IdPost.mine
end 

module User : sig
  (* Should not "see" IdPost.mine_of_t but needs IdUser.current_of_t *)
  val check_password : IdUser.t -> password:string -> IdUser.current
end
Run Code Online (Sandbox Code Playgroud)

有没有一种定义current_of_t : t -> current函数的方法IdUser,只能从模块内调用User

编辑:这是对模块的简化示例,但有一个明显的解决方案,对于一对不能推广到多对,我需要为多对解决这个 - 大约18对,实际上...所以,我将它扩展为对的一个例子.

小智 3

IdUser事实上,存在主义类型也是如此:因为User存在一种 可以让IdUser.current公众IdUser.t接受的类型。有几种方法可以对此进行编码:User如果静态管理依赖关系就足够了,则可以按照 Gasche 所示进行函式化,或者如果需要更多活力,则可以使用一流的模块或对象。

我将进一步研究 Gasche 的示例,为了方便起见,使用私有类型缩写,并展示如何利用半透明性来避免过度私有化实现类型。首先,这可能是一个限制,我想声明一个持久的 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
Run Code Online (Sandbox Code Playgroud)

有了这个,我可以Post使用 s 的具体类型来定义IDs 的类型,但使用 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
Run Code Online (Sandbox Code Playgroud)

与 s 相同User

(* 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
Run Code Online (Sandbox Code Playgroud)

请注意,在这两种情况下,我都使用具体但私有的ID类型。将所有内容结合在一起是一个简单的问题,实际上定义IDADT 及其持久性规则:

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)
Run Code Online (Sandbox Code Playgroud)

此时,为了完全解耦依赖关系,您可能需要签名,USER并且POST可以从该模块导出签名,但添加它们很简单。