参数局部抽象类型

Parametric 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.

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

我相信你想在这里做的事情在 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 应该具有的类型。由于您试图通过 (type m) 抽象 m,因此它应该是:

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

不幸的是,OCaml 不支持更高种类的多态性 允许这种形式的类型 'a 'm。似乎第一个 class 模块类型是精心实现的而不是引入它。

您可以看到以下简短的论文,它解释了 OCaml 中高级多态性的当前(不幸)状态。这解释了一种解决方法:如何在当前的 OCaml 框架中以显式强制转换为代价对其进行编码:

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

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

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

The package-type syntactic class appearing in the ( module package-type ) type expression and in the annotated forms represents a subset of module types. This subset consists of named module types with optional constraints of a limited form: only non-parametrized types can be specified.

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

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

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

如果你想将模块传递给函数,你应该改用仿函数:

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