单个 OCaml 模块导致对接口的假设不一致
Single OCaml module causes inconsistent assumptions about interface
这是我基于 ocamlbuild 的项目结构:
_tags.ml:
true: package(batteries)
Main.mlpack
Stream
Main/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
但是,我不希望子模块发生冲突。
为什么会这样?在我看来,模块可能通过接口与自身发生冲突是不可能的。
BatStream
扩展了 stdlib Stream
模块。冲突可能发生在您的本地 Stream
模块和标准库 Stream
模块之间。
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
中,那么你可以 link 它与另一个模块 M
并且 [=19] 之间不会发生冲突=] 和 P.M
(如果你做的一切都正确)。但是,当您构建包时,构成它的模块不应与它们使用的模块冲突,不幸的是,OCaml 标准库不是包,因此您应该选择不与标准库冲突的名称或您用于实施包的任何其他库。
这是我基于 ocamlbuild 的项目结构:
_tags.ml:
true: package(batteries)
Main.mlpack
Stream
Main/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
但是,我不希望子模块发生冲突。
为什么会这样?在我看来,模块可能通过接口与自身发生冲突是不可能的。
BatStream
扩展了 stdlib Stream
模块。冲突可能发生在您的本地 Stream
模块和标准库 Stream
模块之间。
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
中,那么你可以 link 它与另一个模块 M
并且 [=19] 之间不会发生冲突=] 和 P.M
(如果你做的一切都正确)。但是,当您构建包时,构成它的模块不应与它们使用的模块冲突,不幸的是,OCaml 标准库不是包,因此您应该选择不与标准库冲突的名称或您用于实施包的任何其他库。