2

我正在尝试编写一个基本上如下所示的函数:

module type M = sig
  type t
  val doStuff : t -> unit
end

let f : 'a. 'a -> (module M with type t = 'a) -> unit
      = fun value (module MSomething) -> MSomething.doStuff value

也就是说,一个接受任何类型值的函数,以及一个包含一个或多个可以对该值进行操作的函数的关联模块。不幸的是,上面会让编译器抱怨

此打包模块的类型包含变量

但是,我发现如果我将它包装在一个 GADT 中,我仍然可以让它工作 1)创建'a一个存在和 2)提供从另一个参数化类型变量到存在的转换器:

type 'b gadt =
  GADT: ('b -> 'a) * (module M with type t = 'a) -> 'b gadt

let f value (GADT (convert, (module MSomething))) =
  MSomething.doStuff (convert value)

GADT 本身并不麻烦1但我非常想避免使用该convert功能,因为它除了帮助编译器之外没有任何用途。这有可能吗?

完整示例/MCVE

module type M = sig
  type t
  val doStuff : t -> unit
end

module MInt = struct
  type t = int
  let doStuff = print_int
end

let f : 'a. 'a -> (module M with type t = 'a) -> unit
      = fun value (module MSomething) -> MSomething.doStuff value

let () = f 42 (module MInt : M with type t = int)
let () = print_newline ()

1我实际上想要 GADT,因为我需要用不同的存在参数对模块进行参数化,这样我就可以将不同类型的模块放在一个列表中。但为简单起见,我从上面的第一个示例中省略了这一点。

4

1 回答 1

4

使用一流的模块(如任何本地模块),您应该使用本地抽象类型而不是显式的多态注释:

let f (type a) (value:a) (module M: M with type t = a) = M.doStuff value

工作得很好。

于 2019-10-11T14:15:34.187 回答