我想澄清一点:目前在我看来,在声明一个仿函数时,三重签名复制是必要的,前提是我们将它导出到.mli文件中.这是一个例子:
假设我们有一个仿函数Make,它产生一个A参数化的模块SigA(我能想到的最简单的例子).因此,在.mli文件中,我们有:
module type A = sig
type a
val identity : a -> a
end
module type SigA = sig
type a
end
module Make (MA:SigA) :
A with type a := MA.a
Run Code Online (Sandbox Code Playgroud)
现在我明白我们必须在.ml文件中编写一个实现:
module Make (MA:SigA) = struct
type a = MA.a
let identity obj = obj
end
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都那么好吧?没有!结果我们必须将声明A和SigA逐字复制到.ml文件中:
module type A = sig
type a
val identity : a -> a
end
module type SigA = sig
type a
end
module Make (MA:SigA) = struct
type a = MA.a
let identity obj = obj
end
Run Code Online (Sandbox Code Playgroud)
虽然我(模糊地)理解复制背后的基本原理SigA(毕竟,在源代码中提到它),复制A定义对我来说似乎是一个完全没有意义的练习.我已经对Core代码库进行了简要介绍,他们似乎要么将它复制到小模块中,要么将它们复制到更大的模块中,一旦将它们导出到单独的.mli中,它们同时使用.ml和.mli.
那只是一种事态吗?每个人都可以复制模块签名三次(一次在.mli文件中,两次在.ml文件中:声明和定义!!)目前我正在考虑完全抛弃.mli文件并限制模块导出使用.ml文件中的签名.
编辑:是的我知道我可以通过在.mli文件中声明A内联接口来避免这个问题Make.但是,如果我想从该模块外部使用该接口,这对我没有帮助.
那是因为一对ML和MLI文件就像一个结构和一个匹配的相应签名.
避免两次写出模块类型的常用方法是在单独的ML文件中定义它.例如,
(* sig.ml *)
module type A = sig
type a
end
module type B = sig
type b
val identity : b -> b
end
(* make.mli *)
module Make (A : Sig.A) : Sig.B with type b = A.a
(* make.ml *)
module Make (A : Sig.A) =
struct
type b = A.a
let identity x = x
end
Run Code Online (Sandbox Code Playgroud)
如果它没有隐藏任何东西,就像Sig上面的模块一样,可以省略MLI文件.
在其他情况下,与实现分开写出签名是一个特性,而不是真正的重复 - 它定义了模块的导出,通常,这是实现中的一小部分.
| 归档时间: |
|
| 查看次数: |
739 次 |
| 最近记录: |