在模块中使用“带类型”结构时放松类型检查

时间:2011-03-17 07:51:47

标签: module types ocaml signature

我已经定义了两个模块类型和两个模块

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

然后我将一个仿函数定义为

module Fun (F:FOO) (B:BAR with type t = F.e) = struct type x = string end

(这是一个玩具示例,请忽略仿函数未使用FB这一事实)

现在,如果我定义模块

module Bla = Fun (Foo) (Bar)

我得到了

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

虽然Bar.tFoo.e都定义为int,但OCaml认为Bar.tFoo.e不同。这就是打字系统的工作方式,一般认为这两种类型不同是有意义的(参见Functors and Type Abstraction的最后一段)。

问题有时候我可能希望这会传递类型检查,因为出于我的目的,它们可以被认为是平等的。有没有办法放松一下?


使用gasche建议删除强制,上面的代码可以写成

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)

编译好。奇怪的是,我得到了

# let f x : Bla.x = (x,x);;
val f : Foo.e -> Bla.x = <fun>

问题:为什么推断xFoo.e?它也可能是Bar.t

2 个答案:

答案 0 :(得分:5)

问题在于您如何定义FooBarmodule Foo : FOO = ...。通过在此处强制使用此签名,您可以“密封”模块并使类型抽象化。它无法恢复。您应该在此删除: FOO强制,并在以后需要抽象时使用它。您也可以使用module Foo : (FOO with type e = int) = ...

答案 1 :(得分:0)

我不确定打印机如何在相同类型中选择,但在这种情况下,您可以通过显式注释函数参数来打印不同的名称:

# let f (x:Bar.t) : Bla.x = (x,x);;
val f : Bar.t -> Bla.x = <fun>