views:

88

answers:

2

Hello, how does referenced linking work in OCaml?

Example, let's assume I have 3 modules declared as

  • A.ml
  • B.ml
  • C.ml

of which

  • A needs B and C
  • B needs A

How should I proceed in compiling?

Since order is relevant using ocamlc or ocamlopt how can I fix the cross reference between B and A?

I'm trying to first compile them all into .cmo with ocamlc -c and then link all of them together but with no success since swapping arguments will just move a problem from a module to another.

The specific error is:

Error: Error while linking A.cmo: Reference to undefined global `B'

(or viceversa, if I swap the order of the args)

I thinks this is an easy question but I'm unable to solve it.. thanks in advance

+5  A: 

You have to combine the modules into one file and make them recursive. I don't believe there is a way to do this from the compilation process of two separate files.

module rec A : 
    sig
        val f : int -> int
        val g : int -> int
    end = 
    struct
        let f x = (B.g x) + 1
        let g x = x + 1
    end
and B :
    sig
        val f : int -> int
        val g : int -> int
    end = 
    struct
        let f x = (A.g x) + 1
        let g x = x + 1
    end

EDIT: From your comment, I am guessing you have the type definition of the parser and the functions that process/operate on the type in the same file. I agree with you, it makes sense. But like you've experienced if that file is to not only operate on the type but call the parser to produce the data, how is the parser going to construct it? My solution has been to separate the type into it's own module and open that module in the module that does the operations.

Therefore, you are splitting A into (A and A'), where A' contains the type produced by B, and used in A. Your dependencies become,

  • A needs A' and B and C
  • B needs A'

For example, I have a parser for configuration files I use to start up any application I write.

ConfType   --contains the type t  
Conf       --calls parser, and contains helper functions for type ConfType.t  
ConfParser --for ocamlyacc
ConfLexer  --for ocamllex

An alternative to all this is to use polymorphic variants. In this way you remove the dependency since they are defined ad-hoc. Of course, the type produced by the parser has the potential to be different then the one in Conf, and the compiler wont be able to help you with the error.

nlucaroni
This can be a problem since one of two modules is produces by __ocamlyacc__ so I don't have direct control over `ml/mli` files..
Jack
I think I've run into the same problem. Let me know if I hit it on the head.
nlucaroni
Thank you for your detailed explaination, it really helped a lot to better structure the project :)
Jack
+2  A: 

The Ocaml language supports recursion between modules, but the compiler doesn't support recursion between compilation units. So you can't have A.ml needing B.ml and B.ml needing A.ml. Refactoring to remove the recursion is best if you can do this easily, but let's assume you can't.

One solution, as explained by nlucaroni, is to gather both modules into the same file and use module rec. Another solution sometimes is to turn one module into a functor, e.g., turn A into a functor F which takes an argument with B's signature, and compile first the file defining F, then B, then a file that just defines module A = F(B).

Ocamlyacc does make things more complicated, but you can trick it! You can write module A = functor (...) -> struct in the .mly header and the matching end in the footer. However you'll have to rewrite the generated .mli to add module A : functor (...) -> sig and end as part of your build process. (I know I've done it before, to solve the same problem you're having, though I don't remember where so I can't give a real life example.)

Another possibility worth investigating is switching from Ocamlyacc to Menhir, which is an Ocamlyacc replacement (with little to no porting required as the syntax is the same) with some nice features that could help you, such as support for parametrized parser modules (i.e. functors).

Gilles