无法在递归函数中推断出此打包模块的签名

时间:2018-10-23 17:59:36

标签: module ocaml first-class-modules locally-abstract-type

我仍在尝试找出使用Mirage时如何拆分代码的方法,这是无数一流的模块。 我已将我需要的所有内容放入一个丑陋的Context模块中,以避免必须将十个模块传递给我的所有功能,其中一个已经足够痛苦。

我有一个通过tcp接收命令的功能:

let recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan = ...

经过数小时的尝试和错误,我发现需要添加(type a)和“显式” type chan = a才能使其正常工作。看起来很丑,但是可​​以编译。 但是,如果我想使该函数递归:

let rec recvCmds (type a) (module Ctx : Context with type chan = a) nodeid chan =
  Ctx.readMsg chan >>= fun res ->
  ... more stuff ...
  |> OtherModule.getStorageForId (module Ctx)
  ... more stuff ...
  recvCmds (module Ctx) nodeid chan

我两次通过了模块,第一次没有问题,但是 我在递归行上遇到错误:

The signature for this packaged module couldn't be inferred.

如果我尝试指定签名,我会得到

This expression has type a but an expression was expected of type 'a
The type constructor a would escape its scope

似乎我不能使用整个(type chan = a)东西。 如果有人可以解释正在发生的事情,并且是解决该问题的理想方法,那就太好了。 我当然可以花一会儿时间,但是我宁愿最终了解这些该死的模块。谢谢!

1 个答案:

答案 0 :(得分:2)

实用的答案是递归函数应该使用let rec f: type a. .... = fun ...来统一量化其局部抽象类型。

更准确地说,您的示例可以简化为

module type T = sig type t end 
let rec f (type a) (m: (module T with type t = a)) = f m

产生与您相同的错误:

  

错误:此表达式具有类型(类型为t的模块T = a)          但是应该使用'a类型的表达式          类型构造函数a会逃避其作用域

可以通过显式的forall量化解决此错误:可以使用 缩写符号(用于通用量化的局部抽象类型):

let rec f: type a.  (module T with type t = a) -> 'never = fun m -> f m

此行为背后的原因是本地抽象类型不应转义 介绍它们的功能范围。例如,此代码

let ext_store = ref None
let store x = ext_store := Some x
let f (type a) (x:a) = store x

显然会失败,因为它试图存储类型为a的值,该值是f主体之外的无意义的类型。

因此,具有局部抽象类型的值只能由多态函数使用。例如,这个例子

  let id x = x
  let f (x:a) : a = id x

很好,因为id x适用于任何x

函数之类的问题

 let rec f (type a) (m: (module T with type t = a)) = f m
那么

f的类型尚未在其主体内部进行泛化,因为ML中的类型泛化发生在let定义中。因此,解决方法是向编译器明确告知f的参数是多态的:

 let rec f: 'a. (module T with type t = 'a) -> 'never =
   fun (type a) (m:(module T with type t = a)) -> f m

在这里,'a. ...是一种通用量化,应读为forall 'a. ...。 第一行告诉编译器函数f在其第一个参数中是多态的,而第二行则显式引入了局部抽象类型a来优化打包的模块类型。拆分这两个声明非常冗长,因此速记符号将两者结合在一起:

let rec f: type a.  (module T with type t = a) -> 'never = fun m -> f m