为什么OCaml不能推断出以下类型:

时间:2013-04-15 00:09:21

标签: ocaml type-inference

考虑以下代码

module type Foo = sig 
  type t
  val do_with_t : t -> unit
end

let any_foo t (module F : Foo) = F.do_with_t t

以下可爱类型错误拒绝了:

Error: This expression has type F.t but an expression was expected of type F.t
      The type constructor F.t would escape its scope

但是一旦我添加以下类型注释就被接受了:

let any_foo (type s) (t:s) (module F : Foo with type t = s) = F.do_with_t t

共享约束对我有意义所以我的问题是为什么OCaml不能通过我在函数内使用t来推断类型签名。

2 个答案:

答案 0 :(得分:7)

我不是专家,但这是我对此的看法。

真正的错误是消息的最后一位:

  

类型构造函数F.t将转义其范围

要理解错误消息,首先重写any_foo,不要使用与参数匹配的模式,并重命名参数以使解释更容易理解:

let any_foo arg foo = 
  let (module F : Foo) = foo in
    F.do_with_t arg

您正在使用第一类模块,并将变量foo解压缩到该let语句范围内的模块F。 /强>

现在让我们考虑可以从这个事实推断的参数arg的类型。显然,类型为F.t,但关键是这是一种仅在当前范围内已知的类型,因为module F仅在当前范围内已知。

现在让我们尝试定义生成的any_foo函数的类型:

val any_foo : F.t -> (module Foo) -> unit

还有你的问题,你试图在功能范围内深入揭示新的类型F.t。换句话说,您希望调用者知道您的函数中仅存在的类型。或者,换句话说,您希望类型F.t能够将其范围“转移”给更广泛的受众。

解决方案,解释

现在我们知道了这个问题,我们可以认识到需要向编译器解释这种类型存在于“外部”范围内,并且参数arg属于该类型。

换句话说,我们需要向新创建的模块F添加一个约束,以说明参数arg的类型等于我们新模块中的类型t F。为此我们可以使用局部抽象类型。

继续使用相同的函数,我们可以添加一个本地抽象类型a,并用它约束模块F

let (type a) any_foo arg foo = 
  let (module F : Foo with type t = a) = foo in
    F.do_with_t arg

现在考虑any_foo的类型。

val any_foo : 'a -> (module Foo with type t = 'a) -> unit

没有问题。

为了完整性,让我们回到我们的模式匹配版本:

let (type a) any_foo arg (module F : Foo with type t = a) =
  F.do_with_t arg

答案 1 :(得分:5)

这并没有真正回答你的问题,但在你的情况下,你只需要引入一个新的类型变量s

let any_foo (type s) t (module F : Foo with type t = s) = F.do_with_t t

即。你不需要(t:s)因为类型推断在这里可以正常工作。