2017-02-05 23 views
1

这是我基于ocamlbuild项目结构:单OCaml的模块导致大约接口不一致的假设

_tags.ml:

true: package(batteries) 

Main.mlpack

Stream 

主/ Stream.ml

module MyStream = BatStream 

I a米试图编译使用

ocamlbuild -use-ocamlfind Main.cmo 

错误消息Main模块似乎相当不合逻辑对我说:

+ ocamlfind ocamlc -pack Main/Stream.cmo -o Main.cmo 
File "_none_", line 1: 
Error: The files Main/Stream.cmi and Main/Stream.cmi 
     make inconsistent assumptions over interface Stream 
Command exited with code 2. 
Compilation unsuccessful after building 3 targets (0 cached) in 00:00:00 

这是从OPAM使用的OCaml 4.02.1。

这只发生在与电池连接时,所以我只能算出Batteries.StreamMain.Stream之间有冲突。事实上,如果我添加更多的模块与依赖关系,我可以得到一个消息像

Error: The files /home/ken/.opam/4.02.1/lib/batteries/batteries.cmi 
     and Main/Stream.cmi make inconsistent assumptions 
     over interface Stream 

不过,我不希望子模块发生冲突。

这是怎么发生的?对我来说,模块可能会通过接口与自身发生冲突,这似乎是不可能的。

+0

你有没有尝试过清理(即'室射频_build')? – Drup

+0

@Drup的确如此。包含这三个文件的新项目将生成给定的错误。 –

回答

3

OCaml有编译单元的名称的平面命名空间。当编译单元使用某个模块时,它会记录模块接口的名称和摘要(基本上是接口的CRC)。一致性检查确保两个具有相同名称的接口具有相同的摘要(基本上表示相同的实现)。虽然错误信息确实是误导性的,但它仍然是正确的(尽管措辞可能会好得多)。让我们用ocamlobjinfo工具:

$ ocamlobjinfo _build/Main/Stream.cmi 
File _build/Main/Stream.cmi 
Unit name: Stream 
Interfaces imported: 
     83d31bf1e61f22b62a8b2728a55f2593  Stream 
     d0b21ad0c1f4e93fa8c05b9ded519b52  Stream 
     999b28e3b7638771c87eebf5a8325e42  Pervasives 
     60c2e7663dd57d13b5920931742e1c10  Format 
     9642e3ed163e46770985ca668738ed5f  CamlinternalFormatBasics 
     6dc691300ced97c0e319cbcc0a715044  Bytes 
     3bd1af04573ce2da7fc3dc04403e852e  Buffer 
     383683999ce4d4a54f1689bb92969ecb  BatStream 
     fbefc52bb310bf525973099141e16ffe  BatOrd 
     92bc9ee9d7e3da3421ed7fc5c0ade74d  BatInterfaces 
     7d12ec9e52c91f3af313796ff85158c4  BatInnerIO 
     6f57ab9f63c2f00619c3ffc9bde0bc80  BatIO 
     bd48c0243cabeabfa9ba81aa02319882  BatEnum 
     1972feae99a1525e1b830ca37c4efa20  BatConcurrent 

我们有进口的两个接口具有相同的名称,但不同的实现(CRC数额是不同的)。第一个接口实际上是你Stream模块的接口,第二个是标准的流模块的接口:

$ ocamlobjinfo /home/ivg/.opam/devel/lib/ocaml/stream.cmi 
File /home/ivg/.opam/devel/lib/ocaml/stream.cmi 
Unit name: Stream 
Interfaces imported: 
     d0b21ad0c1f4e93fa8c05b9ded519b52  Stream 
     999b28e3b7638771c87eebf5a8325e42  Pervasives 
     9642e3ed163e46770985ca668738ed5f  CamlinternalFormatBasics 

正如你可能会注意到每个模块始终进口自己的接口。所以冲突在您的Stream模块和OCaml的Stream模块之间。标准的Stream模块通过BatStream模块进入编译单元。

总结。接口命名空间是平坦的,所以你需要使用前缀来防止冲突,参见BatStream。是的,这很丑陋。

模块包装可以帮助您防止被包装到一个封装模块,并使用该包模块之间的名称冲突。举例来说,如果你有装在包P模块M,那么你可以与其他模块M链接它,会有MP.M之间没有冲突(如果你正确地做了一切)。但是,当你建立的包,构成它的模块,不应该有,他们使用的模块冲突,不幸的是,OCaml的标准库是不是一个包,所以你应该选择不与标准库名称冲突或者您正在用于实现您的软件包的任何其他库。

+0

一个很好的解释。谢谢! –

1

BatStream扩展了stdlib Stream模块。冲突可能发生在您的本地Stream模块和stdlib Stream模块之间。

+0

我尽量多的。但是我的*子模块* Main.Stream'不应该和stlib的'Stream'模块冲突。 –

+0

OCaml不使用目录层次结构。你的模块只是'Stream',而不是'Main.Stream'。 – Drup