解压受类型变量约束的 first-class 模块

Unpacking a first-class module constrained by a type variable

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

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

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

The type of this packed module contains variables

但是,我发现如果我将它包装在一个 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 函数,因为它不除了帮助编译器之外,它没有任何其他用途。 这可能以某种方式实现吗?

完整 example/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,因为我需要模块由不同的存在参数化,这样我就可以将不同类型的模块放在一个列表中。但为了简单起见,我在上面的第一个示例中省略了它。

对于第一个 class 模块(就像任何本地模块一样),您应该使用本地抽象类型而不是显式多态注释:

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

工作正常。