Syntax for returning existential types from a function

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:

  1. I have to choose non-conflicting names for all the new types and constructors.
  2. 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” of my_func is far away textually from its actual declaration and definition.
  3. If I don’t carefully keep the names of the type variables in my_func and my_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?

2 Likes

It would be awesome if polymorphic variants could be GADTs:

let my_func : type a b.
  a -> b -> ([ `Result : (a, 'c) foo * (b, 'c) bar -> 'r ] as 'r)

let (`Result (foo, bar)) = my_func a b in ...

But that doesn’t seem likely.