OCaml中的函数:三重代码复制是否必要?

时间:2014-03-12 10:19:28

标签: ocaml functor

我想澄清一点:目前在我看来,在声明仿函数时,三重签名复制是必要的,前提是我们将它导出到.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

到目前为止这么好,对吗?没有!事实证明,我们必须将ASigA逐字的声明复制到.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中,它同时使用.ml和.mli。

这只是一种事态吗?每个人都可以复制模块签名三次(一次在.mli文件中,两次在.ml文件中:声明和定义!!) 目前我正在考虑完全放弃.mli文件并使用.ml文件中的签名限制模块导出。


编辑:是的我知道我可以通过在.mli文件中声明A内嵌Make的内联接口来避免此问题。但是,如果我想从该模块外部使用该接口,这对我没有帮助。

1 个答案:

答案 0 :(得分:5)

那是因为一对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模块。

在其他情况下,与实现分开写出签名是一个功能,而不是真正的重复 - 它定义了模块的导出,通常,这是实现中的一小部分。