将模块及其实例作为OCaml函数的参数
我想编写一个函数,该函数采用实现特定签名的模块和与这些模块相同类型的实例,但显然我不能这样做,因为与模块范围相关的问题(模块及其实例是两个参数,因此实例不知道模块的类型)。
下面是一个例子:
let f (type a) (module M: Set.S with type elt = a) (pr: a -> unit) (m: M.t) =
M.iter pr m;;
其中M是具有 type 元素的 Set 模块a,并且pr可以是 type 元素的打印机a。以及由此引起的错误消息(我觉得不是很清楚):
Line 1, characters 69-78:
Error: This pattern matches values of type M.t
but a pattern was expected which matches values of type 'a
The type constructor M.t would escape its scope
我试图通过考虑问题是由仅覆盖函数体的参数范围引起的来解决这个问题,所以我将最后一个参数放在函数体中,如下所示:
let f (type a) (module M: Set.S with type elt = a) (pr : a -> unit) =
fun (m : M.t) ->
M.iter pr m;;
但错误信息仍然存在:
Line 2, characters 7-16:
Error: This pattern matches values of type M.t
but a pattern was expected which matches values of type 'a
The type constructor M.t would escape its scope
那么有没有办法做到呢?
回答
OCaml 核心语言(模块系统之外)不是依赖类型的。在幻想语法中,您的函数将具有类型 function (module M: Set.S with type elt = 'a) -> ('a -> unit) -> M.t。在这种类型中,M是一个值,因此该类型是依赖类型的,不能在 OCaml 中实现。
在你的情况下,有可能使类型不依赖通过限制接受的参数与类模块的with约束
let f (type a t ) (module M: Set.S with type elt = a and type t = t)
pr m = M.iter pr m
module String_set = Set.Make(String)
let () = f (module String_set) ignore String_set.empty
另一种可能的解决方案是将值与第一类模块及其存在量化一起存储:
module type packed = sig
type a
module Impl: Set.S with type elt = a
val value: Impl.t
end
let g (type a) (module P: packed with type a = a)
pr = P.Impl.iter pr P.value
但是对于更复杂的函数,除了在模块级别使用函子之外别无选择。
旁白:如果你想知道为什么Set.S with type elt = a and type t = t上面第一个变体中的模块类型是一个(必要的)限制,请考虑这个打包模块:
let random_int_set: (module Set.S with type elt = int) =
let compare =
if Random.int 3 > 1 then Stdlib.compare
else (fun x y -> Stdlib.compare y x)
in
let module S = Set.Make(struct type t = int let compare = compare end) in
(module S)
这里,集合类型基于随机compare函数。因此集合的类型与所有其他Sets不兼容。因此,只能使用带有打包值的模块:
module P = struct
type a = int
module Impl = (val random_int_set)
let value = Impl.empty
end
let () = g (module P) ignore