这是我基于ocamlbuild的项目结构:
_tags.ml:
true: package(batteries)
Main.mlpack
Stream
主/ Stream.ml
module MyStream = BatStream
我正在尝试使用
编译Main
模块
ocamlbuild -use-ocamlfind Main.cmo
错误消息对我来说似乎不合逻辑:
+ 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.Stream
和Main.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 :(得分: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
模块的接口,第二个接口是标准的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
。是的,很难看。
模块打包可以帮助您防止打包到程序包中的模块与使用该程序包的模块之间的名称冲突。例如,如果您在软件包M
中打包了一个模块P
,那么您可以将其与另一个模块M
相关联,M
和{之间不会发生冲突{1}}(如果你做的一切都正确)。但是,当您构建软件包时,构成软件包的模块不应与它们使用的模块发生冲突,并且不幸的是,OCaml标准库不是软件包,因此您应该选择不会与软件包冲突的名称。标准库或您用于实现程序包的任何其他库。
答案 1 :(得分:1)
BatStream
扩展了stdlib Stream
模块。您的本地Stream
模块和stdlib Stream
模块之间可能存在冲突。