当从多个模块创建一个库时,我找不到一种很好的方法来向库的用户(外部接口)隐藏正确的信息,同时能够在内部接口上访问我需要的一切。
更具体地说,我有两个模块(文件 a.ml[i] 和 b.ml[i])。在 A 中,我定义了一些类型 t,这是我不想向用户隐藏的内部结构(外部接口)。
module A : sig
type t
end
module A = struct
type t = float
end
Run Code Online (Sandbox Code Playgroud)
在模块B,然后我想用秘密类型A.t。
module B : sig
create_a : float -> A.t
end
module B = struct
create_a x = x
end
Run Code Online (Sandbox Code Playgroud)
这当然不会编译,因为 B 的编译单元不知道A.t.
我知道但不喜欢的解决方案:
create_a移至模块AA.tto的定义B并用一些欺骗类型检查器external cheat : `a -> `b = "%identity"有没有其他方法可以A.t在B不将此信息泄漏到图书馆界面的情况下了解in的类型?
一如既往,额外的间接层可以解决这个问题。定义一个Lib将指定外部接口的模块,例如,
module Lib : sig
module A : sig
type t
(* public interface *)
end
module B : sig
type t
(* public interface *)
end = struct
module A = A
module B = B
end
Run Code Online (Sandbox Code Playgroud)
如果您不想重复自己并编写两次模块签名,那么您可以在一个模块中定义一次sigs.ml:
module Sigs = struct
module type A = sig
type t
(* public interface *)
end
(* alternatively, you can move it into sigs_priv.ml *)
module type A_private = sig
include A
val create_a : float -> t
end
...
end
Run Code Online (Sandbox Code Playgroud)
最后,确保.cmi在安装步骤中您没有安装接口(文件),以便用户无法绕过您的抽象。如果您正在使用 oasis,那么这很简单:只需将所有模块内置于内部,除了 module Lib,即,使用InternalModules字段指定它们。