Interface OCaml类型声明中的冗余(ml/mli)

Interface OCaml类型声明中的冗余(ml/mli),interface,module,ocaml,declaration,Interface,Module,Ocaml,Declaration,我试图了解有关ocaml模块及其编译的一个具体内容: 我是否被迫在特定的.ml实现中重新声明已在.mli中声明的类型 举个例子: (* foo.mli *) type foobar = Bool of bool | Float of float | Int of int (* foo.ml *) type baz = foobar option 根据我对接口/实现的正常思考方式,这应该是可以的,但它说 错误:未绑定类型构造函数foobar 在试图用 ocamlc -c foo.mli oca

我试图了解有关ocaml模块及其编译的一个具体内容:

我是否被迫在特定的
.ml
实现中重新声明已在
.mli
中声明的类型

举个例子:

(* foo.mli *)
type foobar = Bool of bool | Float of float | Int of int

(* foo.ml *)
type baz = foobar option
根据我对接口/实现的正常思考方式,这应该是可以的,但它说

错误:未绑定类型构造函数foobar

在试图用

ocamlc -c foo.mli
ocamlc -c foo.ml
当然,如果我也在
foo.ml
中声明
foobar
,错误就会消失,但这似乎是一种复杂的方式,因为我必须在每次更改时保持同步

有没有办法避免这种冗余,或者我每次都被迫重新声明类型


提前感谢

是的,您被迫重新声明类型。我所知道的唯一的解决办法是

  • 不要使用.mli文件;只需公开没有接口的所有内容。糟糕的主意

  • 使用可读的编程工具或其他预处理器避免在一个真实源中重复接口声明。对于大型项目,我们在我的团队中进行


对于小型项目,我们只是复制类型声明。你可以让ocamlc从ml文件中为你生成mli文件:

ocamlc -i some.ml > some.mli

一般来说,是的,您需要复制类型

但是,您可以使用Camlp4和
pa_宏
语法扩展(findlib包:
Camlp4.macro
)来解决这个问题。除其他外,它定义并包括构造。您可以使用它将公共类型定义分解成一个单独的文件,并将该文件包括在
.ml
.mli
文件中。然而,我还没有在已部署的OCaml项目中看到这一点,所以我不知道这是否符合推荐做法,但这是可能的


但是,读写编程解决方案更干净。

OCaml试图迫使您将接口(
.mli
)与实现分离(
.ml
。大多数情况下,这是一件好事;对于值,您在接口中发布类型,并将代码保留在实现中。您可以说OCaml正在强制执行一定量的抽象(必须发布接口;接口中没有代码)

对于类型,实现通常与接口相同:两者都声明类型具有特定的表示(并且可能类型声明是生成的)。在这里,不可能有抽象,因为实现者没有关于他不想发布的类型的任何信息。(例外情况基本上是在声明抽象类型时。)

一种方法是,接口已经包含足够的信息来编写实现。给定接口
type foobar=Bool of Bool | Float of Float | Int of Int
,只有一种可能的实现。因此不要编写实现

一个常见的习惯用法是使用一个专用于类型声明的模块,并使其仅具有一个
.mli
。由于类型不依赖于值,因此此模块通常在依赖项链的早期出现。大多数编译工具都能很好地处理此问题;例如
ocamldep
。(这是比只有
.ml
的一个优势)


这种方法的局限性在于,当您还需要一些模块定义时(一个典型的例子是定义一个类型
foo
,然后定义一个
OrderedFoo:Map.OrderedType
模块和
type t=foo
,然后定义另一个类型声明,其中涉及
“Map.Make(OrderedFoo).t
)这些不能放在接口文件中。有时可以将定义分解成几个块,首先是一组类型(
types1.mli
),然后是一个模块(
mod1.mli
mod1.ml
),然后是更多类型(
types2.mli
)。其他时候(例如,如果定义是递归的)您必须使用
.ml
而不使用
.mli
或复制。不,在mli文件中,只需说“键入foobar”。这将起作用。

我认为允许
.ml
文件推断耦合
.mli
中描述的类型不应该有任何限制。据我所知,实际实现强制冗余,但两个签名必须相等,因此这实际上是相同声明的两倍。这就是为什么我认为至少,它应该知道这些声明,而不必复制和粘贴它们。根据我的说法,类型推断算法不会出现问题。它不会强制冗余,也不要求签名相同,只是ml文件中的声明必须与mli声明相同或更具体e mli是定义在接口中可见的内容,因此您可以选择不公开类型(在这种情况下,它不在mli文件中),也可以选择公开存在类型,但不公开它的使用方式(在这种情况下,类型声明不同)。当然,在您的情况下,编译器只假设该类型是有意义的,因为它在mli中是完全定义的。@Niki在值声明中没有强制冗余(无论如何在.ml中是可选的),而在实践中,这就是“至少与”但在99%的情况下,接口中声明的清单类型与实现中的类型定义完全相同。这是多余的,而且令人恼火,但作为一名语言设计师,我已经仔细考虑了这个问题,我还没有提出一个我认为既有原则又明显优于OCaml的建议。@Niki我可以理解,您可能希望强制使用更窄的类型作为值定义的参数,但这种情况不会发生在类型上,因为通常情况下它们都是