单个OCaml模块导致关于接口的不一致假设

时间:2017-02-05 08:03:26

标签: ocaml ocamlbuild

这是我基于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.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

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

为什么会这样?对我来说,模块可能在界面上与自身冲突似乎是不可能的。

2 个答案:

答案 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模块之间可能存在冲突。