Я пытаюсь написать функцию, которая в основном выглядит следующим образом:
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, потому что мне нужно, чтобы модуль был параметризован другим экзистенциалом, чтобы я мог соединить модули разных типовв списке. Но для простоты я опустил это в первом примере выше.