When working with parametrized types I often find myself doing things like this to “return an existential type” from a function:
type (_, _) my_func_result =
My_func_result : ('a, 'c) foo * ('b, 'c) bar -> ('a, 'b) my_func_result
let my_func : type a b. a -> b -> (a, b) my_func_result = ...
let (My_func_result (foo, bar)) = my_func a b in ...
I’m glad that I can do this, but the boilerplate is annoying because:
- I have to choose non-conflicting names for all the new types and constructors.
- If
my_func
is defined mutually recursively with a lot of other functions, all their result types have to be defined first, so the “actual return type” ofmy_func
is far away textually from its actual declaration and definition. - If I don’t carefully keep the names of the type variables in
my_func
andmy_func_result
in sync (or even if I do), it can be hard to figure out from reading the code exactly what the return type will be for certain input types.
I’m wondering whether there is any way to simplify this. For instance, would it be possible to write a PPX that would generate the above code from something like this?
let my_func : type a b. a -> b -> [%exists 'c. (a, 'c) foo * (b, 'c) bar] = ...
let%exists (foo, bar) = my_func a b in ...
Or is there a better way to deal with this?