我试图弄清楚如何根据具有参数类型的模块编写函数,但我无法在任何地方找到类似的东西。我试图尽可能地减少问题,最后得到了这个虚拟的例子。
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.
执行此操作的正确语法是什么?
答案 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