2

Here is my ocamlbuild-based project structure:

_tags.ml:

true: package(batteries)

Main.mlpack

Stream

Main/Stream.ml

module MyStream = BatStream

I am trying to compile the Main module using

ocamlbuild -use-ocamlfind Main.cmo

The error message seems rather illogical to me:

+ 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

This is using OCaml 4.02.1 from OPAM.

This only happens when when linking with Batteries, so I can only figure that there is a conflict between Batteries.Stream and Main.Stream. Indeed, if I add more modules with dependencies, I can get a message like

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

However, I would not expect submodules to clash.

Why is this happening? It seems impossible to me that a module may conflict with itself over an interface.

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72

2 Answers2

4

OCaml has a flat namespace for compilation unit's names. When a compilation unit uses some module, it records the name of the module interface and a digest (basically an CRC of the interface). A consistency check ensures that two interfaces with the same name have the same digest (basically represent the same implementation). Although the error message is indeed misleading, it is still correct (although the wording could be much better). Let's use the ocamlobjinfo tool:

$ 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

We have two interfaces imported that have the same name, but different implementations (CRC sums are different). The first interface is actually the interface of your Stream module, the second one is the interface of the standard's Stream module:

$ 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

As you may notice each module always imports its own interface. So the conflict is between your Stream module and OCaml's Stream module. The standard's Stream module got into your compilation unit via the BatStream module.

To summarize. Interface namespace is flat, so you need to use prefixed to prevent conflicts, cf., BatStream. Yes, it is ugly.

Module packing can help you in preventing name conflicts between modules that are packed into a package, and modules that use the package. For example, if you have a module M packed in the package P, then you can link it with another module M and there will be no conflicts between M and P.M (if you did everything correctly). However, when you build the package, the modules constituting it, should not have conflicts with modules that they use, and, unfortunately, the OCaml standard library is not a package, so you should choose names that don't collide with the standard library or any other library that you are using for the implementation of your package.

ivg
  • 34,431
  • 2
  • 35
  • 63
1

BatStream extends the stdlib Stream module. The conflict is likely between your local Stream module and the stdlib Stream module.

hcarty
  • 1,671
  • 8
  • 8