I have a likely newbie question on the module system, assume this program:
module U : sig type t end = struct type t = int end
module S : sig type t = C of U.t end = struct type t = C of U.t end
module T = struct
type s = S.t =
| C of U.t
end
module API : sig
module U : module type of U
module S : module type of S
module T : sig
type s = S.t
(* = C of U.t *)
end
end = struct
module U = U
module S = S
module T = T
end
Up to this point, everything goes OK, however, if I uncomment the C of U.t bit, then OCaml complains with
This variant or record definition does not match that of type S.t
The types for field C are not equal.
That’s fairly strange as ocamlc -i gives me the interface for the original version:
module U : sig type t end
module S : sig type t = C of U.t end
module T : sig type s = S.t = C of U.t end
module API :
sig
module U : sig type t end
module S : sig type t = C of U.t end
module T : sig type s = S.t end
end
which in principle compiles fine if I add the type constraint. Thanks!
This a problem of name catpure: U.t refers to two different types within the program.
More precisely, the problem starts at that line
module S : sig type t = C of U.t end = struct type t = C of U.t end
Here, the definition of S.t captures the concrete type U.t defined at line 1.
Contrarily when defining the submodule API.T
module T : sig
type s = S.t = C of U.t
end
U.t refers to the submodule API.U defined at line 9. There is therefore a type error since S.t =C of U[line 1].t ≠C of U[line 9].t.
That’s fairly strange as ocamlc -i gives me the interface for the original version:
Unfortunately, the interfaces printed by ocamlc -i are not always equal to the interface actually inferred by the compiler due to these problems of name capture. Using a version of the compiler patched to make these problems more explicit yields for ocamlc -i test.ml:
Warning 63: The printed interface differs from the inferred interface.
The inferred interface contained items which could not be printed
properly due to name collisions between identifiers.
File “test.ml”, line 9, characters 2-29:
Definition of module U/1
File “test.ml”, line 1, characters 0-67:
Definition of module U/2
Beware that this warning is purely informational and will not catch all
instances of erroneous printed interface.
module U : sig type t end
module S : sig type t = C of U.t end
module T : sig type s = S.t = C of U.t end
module API :
sig
module U : sig type t end
module S : sig type t = C of U/2.t end
module T : sig type s = S.t end
end
Oh indeed I see it now, thanks @octachron; I was not sure how references to types where resolved when using module type of and now I see they are preserved, however the printing confused me indeed. I will have to add the proper equalities then.
I don’t know in which context you want to use such a pattern, but maybe you can transform the outer modules S ans T into functors to capture the dependency with U.t. This example compile :
module U : sig type t end = struct type t = int end
module S (U : module type of U) : sig type t = C of U.t end = struct
type t = C of U.t
end
module T (U : module type of U) = struct
type s = S(U).t = C of U.t
end
module API : sig
module U : module type of U
module S : module type of S(U)
module T : sig
type s = S.t
= C of U.t
end
end = struct
module U = U
module S = S(U)
module T = T(U)
end
I’ve never met the syntax type s = S.t = C of U.t before this thread, what is its rationale ? Why not simply write type s = S.t ?
Thanks for the hint, indeed I could use functors, but here I am in the context of trying ot make “a module overlay” over a large existing codebase so functorializing hundreths of modules is not an option ATM.
Indeed that’s in the Coq codebase. I would like to redeclare signatures in order to better organize them, so if I have:
module A : sig type t end
module B : sig type t = A.t end
I would like to have a new API.A.t and redeclare API.B.t such that it refers to API.A.t and the equality is lost (as to ensure encapsulation). However it looks like either functors should be used or some custom tool (that I’m not willing to write :D)
I’m not sure I understand your intention : do you want to hide the equality between API.A.t and API.B.t (and ensure encapsulation) or do you want to show it ? In the example of you first message the equality is known outside the API module.
BTW, you can achieve what you try to do in your first message without functors but you have to not use module type of and rewrite explicitly the signature of each modules, as in this example :
module API : sig
module U : sig type t end
module S : sig type t = C of U.t end
module T : sig
type s = S.t
= C of U.t
end
end = struct
module U = U
module S = S
module T = T
end
Ok, I better understand what you want. This is indeed the case A.t and API.A.t are not compatible types, as we can see in this example :
module A : sig
type t
val v : t
end = struct
type t = A
let v = A
end
module API : sig
module A : module type of A
end = struct
module A = A
end
A.v = API.A.v;;
Error: This expression has type API.A.t but an expression was expected of type
A.t
I don’t see how you can do what you want without manually write this .mli file. For example, the Univ.Level module and the API.Univ.Level one don’t have the same signature. If the use of module type of worked, the modules would have the same signature.