Relaxing type checking when using 'with type' construction in modules
I have defined two module types and two modules
module type FOO = sig type e end
module type BAR = sig type t end
module Foo : FOO = struct type e = int end
module Bar : BAR = struct type t = int end
开发者_运维百科
Then I define a functor as
module Fun (F:FOO) (B:BAR with type t = F.e) = struct type x = string end
(this is a toy example, please ignore the fact that F
and B
are not used by the functor)
Now, if I define the module
module Bla = Fun (Foo) (Bar)
I get
Error: Signature mismatch:
Modules do not match:
sig type t = Bar.t end
is not included in
sig type t = Foo.e end
Type declarations do not match:
type t = Bar.t
is not included in
type t = Foo.e
Although both Bar.t
and Foo.e
are defined as int
OCaml considers Bar.t
and Foo.e
to be different. That's just the way the typing system works and it makes sense to consider these two types different in general (c.f. last paragraph of Functors and Type Abstraction).
Question: Sometimes I may want this to pass type checking because for my purposes they can be considered equal. Is there a way to relax this?
Using gasche's suggestion of removing coercion, the above code can be written as
module type FOO = sig type e end
module type BAR = sig type t end
module Foo = struct type e = int end
module Bar = struct type t = int end
module Fun (F : FOO with type e=int) (B : BAR with type t = int) = struct type x = F.e * B.t end
module Bla = Fun (Foo) (Bar)
which compiles fine. Strangely, I get
# let f x : Bla.x = (x,x);;
val f : Foo.e -> Bla.x = <fun>
Question: why does it infer that x
is Foo.e
? It could as well be Bar.t
?
The problem is how you define Foo
and Bar
: module Foo : FOO = ...
. By imposing this signature here, you "seal" the module and make the type abstract. It cannot be reverted. You should remove the : FOO
coercion here, and use it later when you need the abstraction. You could also use module Foo : (FOO with type e = int) = ...
.
I'm not sure how the printer chooses amongst equal types, but in this case you can cause it to print a different name by explicitly annotating your function argument:
# let f (x:Bar.t) : Bla.x = (x,x);;
val f : Bar.t -> Bla.x = <fun>
精彩评论