The explanation in Section 8.21 Generative Functors of the OCaml Manual gives the impression that using () or struct end as functor argument make a functor generative. The code below makes me suspect that any functor argument that is not a named module makes a functor generative. What are the details here?
module X (E: sig type x end): sig
type t
val t: t
end = struct
type t = int
let t = 0
end
module X1 = X (struct type x = int end)
module X2 = X (struct type x = int end)
module E = struct
type x = int
end
module X1' = X (E)
module X2' = X (E)
Types X1.t and X2.t are incompatible – X is generative
Types X1'.t anf X2'.t are compatible – X is not generative
I think the right thing to say is that, first, generativity is a property of the functor itself, not of any particular application of that functor; in particular, your functor X is applicative since it doesn’t have any () parameter.
Second, what’s happening in your X1 and X2 is that the parameters are two different modules (even though they happen to have the same implementation), so that the resulting modules contain different types. EDIT: You can see the same behavior if you bind module P1 = struct type x = int end and module P2 = struct type x = int end and then apply X to P1 and P2.
However, I’m no module expert, so take this with a grain of salt until one comes along.
The semantic of functor in OCaml is applicative : a functor applied to the same path always returns compatible abstract types. But if you apply to anonymous structure, it will return incompatible abstract types. This is distinct to the semantic of functors in SML where functors are always generative (X1'.t and X2'.t will be incompatible in SML).
This extension is just a kind of syntactic sugar to have generative functors in OCaml when applied to the same path.
module type S = sig type t val x : t end
module F (M : sig end) : S = struct
type t = int
let x = 1
end
module F_gen (M : sig end) () : S = F (M)
module E = struct end;;
module E : sig end
module X1 = F (E);;
module X1 : sig type t = F(E).t val x : t end
module X2 = F (E);;
module X2 : sig type t = X1.t val x : t end
module X1' = F_gen (E) ();;
module X1' : S
module X2' = F_gen (E) ();;
module X2' : S
here X1.t and X2.t are compatible, but X1'.t and X2'.t are not.
You can read this Xavier Leroy’s paper (section 1 and 2) to understand the motivation behind applicative semantic.