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

Geo*_*kov 5 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
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)

到目前为止一切都那么好吧?没有!结果我们必须将声明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
Run Code Online (Sandbox Code Playgroud)

虽然我(模糊地)理解复制背后的基本原理SigA(毕竟,在源代码中提到它),复制A定义对我来说似乎是一个完全没有意义的练习.我已经对Core代码库进行了简要介绍,他们似乎要么将它复制到小模块中,要么将它们复制到更大的模块中,一旦将它们导出到单独的.mli中,它们同时使用.ml和.mli.

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


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

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

如果它没有隐藏任何东西,就像Sig上面的模块一样,可以省略MLI文件.

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

  • @AndreasRossberg 一个单独的独立“.mli”确实可以工作,只要其中没有值组件。 (2认同)