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

时间:2010-07-13 15:04:01

标签: interface module ocaml declaration

我正在尝试理解有关ocaml模块及其编译的具体事项:

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

举一个例子:

(* 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

当然,如果我在foobar内声明foo.ml,错误就会消失,但这似乎很复杂,因为我必须在每次更改时保持同步。

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

提前致谢

5 个答案:

答案 0 :(得分:18)

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

  • 不要使用.mli文件;只是暴露一切没有界面。可怕的想法。

  • 使用文字编程工具或其他预处理器来避免重复One True Source中的接口声明。对于大型项目,我们在我的小组中执行此操作。

对于小型项目,我们只是复制类型声明。抱怨它。

答案 1 :(得分:17)

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,然后是另一个涉及'a Map.Make(OrderedFoo).t的类型声明。)这些不能放在接口中文件。有时可以将您的定义分解为几个块,首先是一堆类型(types1.mli),然后是一个模块(mod1.mlimod1.ml),然后是更多类型({{1} })。其他时候(例如,如果定义是递归的),您必须使用types2.mli而不是.ml或重复。

答案 2 :(得分:13)

您可以让ocamlc从ml文件中为您生成mli文件:

ocamlc -i some.ml > some.mli

答案 3 :(得分:3)

一般情况下,是的,您需要复制类型。

但是,您可以使用Camlp4和pa_macro语法扩展(findlib包:camlp4.macro)解决此问题。除其他外,它定义了INCLUDE和INCLUDE构造。您可以使用它将公共类型定义分解为单独的文件,并将该文件包含在.ml.mli文件中。我没有在已部署的OCaml项目中看到这样做,所以我不知道它是否符合推荐的做法,但它是可能的。

然而,有文化的编程解决方案是更清洁的IMO。

答案 4 :(得分:-3)

不,在mli文件中,只说“type foobar”。这将有效。