将模块及其实例作为 OCaml 函数的参数
Having a module and an instance of it as parameters to an OCaml function
我想编写一个函数,它采用实现特定签名的模块和与这些模块相同类型的实例,但显然我不能这样做,因为与模块范围相关的问题(模块和它的实例都是参数,因此实例不知道模块的类型。
这是一个例子:
let f (type a) (module M: Set.S with type elt = a) (pr: a -> unit) (m: M.t) =
M.iter pr m;;
其中 M
是具有 a
类型元素的 Set 模块,pr
可以是 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
约束
限制接受作为参数的模块的 class 来使类型不依赖
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
另一种可能的解决方案是将值与第一个 class 模块及其存在量化一起存储:
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
函数。因此集合的类型与所有其他 Set
不兼容。因此,只能使用具有打包值的此类模块:
module P = struct
type a = int
module Impl = (val random_int_set)
let value = Impl.empty
end
let () = g (module P) ignore
我想编写一个函数,它采用实现特定签名的模块和与这些模块相同类型的实例,但显然我不能这样做,因为与模块范围相关的问题(模块和它的实例都是参数,因此实例不知道模块的类型。
这是一个例子:
let f (type a) (module M: Set.S with type elt = a) (pr: a -> unit) (m: M.t) =
M.iter pr m;;
其中 M
是具有 a
类型元素的 Set 模块,pr
可以是 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
另一种可能的解决方案是将值与第一个 class 模块及其存在量化一起存储:
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
函数。因此集合的类型与所有其他 Set
不兼容。因此,只能使用具有打包值的此类模块:
module P = struct
type a = int
module Impl = (val random_int_set)
let value = Impl.empty
end
let () = g (module P) ignore