我想澄清一点:目前,在我看来,在声明仿函数的同时,必须将三重签名重复,只要我们将其导出到.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
现在,我了解到我们必须在.ml文件中编写一个实现:
module Make (MA:SigA) = struct
type a = MA.a
let identity obj = obj
end
到目前为止一切都很好,对吗?不!原来,我们必须将
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
虽然我(模糊地)理解了复制
SigA
的基本原理(毕竟,在源代码中已经提到过),但是复制A
定义对我来说似乎完全没有意义。我已经简要浏览了Core代码库,一旦将它们导出到单独的.mli中,它们似乎要么将其复制到小型模块中,要么复制到更大的模块中,该.mli可从.ml和.mli中使用。
那仅仅是一种状况吗?每个人都可以复制模块签名3次吗(一次在.mli文件中,一次在.ml文件中两次:声明和定义!)
目前,我正在考虑完全放弃.mli文件,并使用.ml文件中的签名限制模块导出。
编辑:是的,我知道我可以通过在.mli文件中的
A
内声明Make
内联接口(interface)来避免此问题。但是,如果我想从该模块外部使用该接口(interface),这对我没有帮助。 最佳答案
这是因为一对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
在不隐藏任何内容的情况下,最好忽略MLI文件,例如上面的
Sig
模块。在其他情况下,将签名与实现分开写入是一项功能,而不是真正的重复-它定义了模块的导出,通常,这只是实现中的一小部分。
关于ocaml - OCaml中的仿函数: triple code duplication necessary?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/22348341/