参数化本地抽象类型

时间:2016-02-03 11:08:59

标签: ocaml locally-abstract-type

我试图弄清楚如何根据具有参数类型的模块编写函数,但我无法在任何地方找到类似的东西。我试图尽可能地减少问题,最后得到了这个虚拟的例子。

module type Mappable = sig
  type 'a t
  val map : ('a -> 'b) -> 'a t -> 'b t
end

let plus (type m) (module M : Mappable with type 'a t = 'a m) (xs : int m) = 
  M.map (fun x -> x + 1) xs

产生错误Error: Syntax error: module-expr expected

如果我放弃'a,我最终会出现以下错误。

Error: In this `with' constraint, the new definition of t
       does not match its original definition in the constrained signature:
       Type declarations do not match: type t is not included in type 'a t
       They have different arities.

执行此操作的正确语法是什么?

3 个答案:

答案 0 :(得分:7)

我相信你想要做的事情在OCaml 4.02.3中是不可能的。让我们看一个没有类型变量的简化版本:

module type Mappable = sig
  type t
  val map : ('a -> 'b) -> t -> t
end

let plus (type m) (module M : Mappable with type t = m) (xs : m) = 
  M.map (fun x -> x + 1) xs

上述内容是典型的,plus具有以下类型:

val plus : (module Mappable with type t = 'm) -> 'm -> 'm

其定义中的m类型被抽象为变量'm

现在,回到原始代码并考虑plus类型应该具有的内容。由于您试图通过m抽象(type m),因此它应该是:

val plus : (module Mappable with type 'a t = 'a 'm) -> 'a 'm -> 'a 'm

不幸的是,OCaml不支持更高的kinded多态性 允许使用'a 'm这种形式。似乎第一类模块输入是经过精心实施而不是为了介绍它。

您可以看到以下简短的文章,该文章解释了OCaml中较高亲缘多态性的当前(不幸)状态。这解释了一种解决方法:如何在当前的OCaml框架中对其进行编码,并花费明确的代价:

https://ocamllabs.github.io/higher/lightweight-higher-kinded-polymorphism.pdf

我自己从未尝试过,但同样的解决方法可以应用于您的示例。

答案 1 :(得分:6)

在OCaml中是不可能的,因为类型约束不是常规模块类型约束,而是一个特殊的syntactic construct,它不允许多态类型:

  

出现在(module package-type)类型表达式和带注释的表单中的包类型语法类表示模块类型的子集。该子集由具有可选约束形式约束的命名模块类型组成:只能指定非参数化类型。

通常的解决方法是创建一个模块,将所有类型变量绑定到具体类型:

module type Mapper = sig 
  type a
  type b
  type src
  type dst
  val map : (a -> b) -> src -> dst
end

let plus (type src) (type dst)
    (module M : Mapper with type dst = dst
                        and type src = src
                        and type a = int
                        and type b = int) (xs : src) : dst =
  M.map (fun x -> x + 1) xs

在您的特定示例中,无需绑定'a'b,因为它们基本上未被使用,因此可以简化为:

module type Mapper = sig 
  type src
  type dst
  val map : ('a -> 'b) -> src -> dst
end

let plus (type src) (type dst)
    (module M : Mapper with type dst = dst
                        and type src = src) (xs : src) : dst =
  M.map (fun x -> x + 1) xs

当然,这是非常有限的,但这是当天可能的。

答案 2 :(得分:2)

如果要将模块传递给函数,则应使用仿函数:

module F (M : Mappable) = struct
  let plus xs = M.map (fun x -> x + 1) xs
end