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

Jac*_*ack 32 ocaml module interface declaration

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

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

举个例子:

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

(* foo.ml *)
type baz = foobar option
Run Code Online (Sandbox Code Playgroud)

根据我对接口/实现的正常思考方式,这应该没问题,但它说

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

在尝试编译时

ocamlc -c foo.mli
ocamlc -c foo.ml
Run Code Online (Sandbox Code Playgroud)

当然,如果我申报错误消失foobar里面foo.ml太多,但它似乎是一个复杂的方式,因为我要保持同步的东西在每一个变化.

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

提前致谢

Nor*_*sey 18

是的,你被迫重新宣布类型.我知道的唯一方法是

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

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

对于小型项目,我们只需复制类型声明.并抱怨它.

  • 它不会强制冗余,也不需要签名相同,只是ml文件中的声明必须与mli声明相同或更具体.mli的要点是定义接口中可见的内容,因此您可以选择不公开类型(在这种情况下它不在mli文件中)或者您可以选择公开有类型,但不是如何它被使用(在这种情况下,类型声明是不同的).当然,在您的情况下,编译器只需假设类型就有意义,因为它在mli中已完全定义. (4认同)
  • @Niki在*value*声明中没有强制冗余(无论如何在.ml中是可选的),并且在实践中,"至少具体如"的地方.但在99%的情况下,*manifest声明类型*声明在接口与实现中的类型定义相同.这是多余的,令人恼火,但作为一名语言设计师,我一直在努力思考这个问题,而且我还没有提出一个我认为既有原则又明显优于OCaml的提案. (3认同)
  • 实际上恰恰相反——类型在结构及其签名中指定不同的表示形式是很常见的。有关摘要,请参阅 http://caml.inria.fr/pub/docs/manual-ocaml/manual016.html#toc54。 (2认同)

Gil*_*il' 17

OCaml试图强制你将interface(.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),然后是更多类型(types2.mli).其他时候(例如,如果定义是递归的),您必须使用.ml没有.mli或重复的.


ane*_*eal 14

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

ocamlc -i some.ml > some.mli
Run Code Online (Sandbox Code Playgroud)