我正在尝试编写一个基本上如下所示的函数:
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,因为我需要用不同的存在参数对模块进行参数化,这样我就可以将不同类型的模块放在一个列表中。但为简单起见,我从上面的第一个示例中省略了这一点。