When using GADTs I often find myself needing to give a type annotation that’s impossible to give because it involves existential types that I don’t have a name for. For instance, consider the following code:
type bar = private Dummy_bar
type baz = private Dummy_baz
type _ foo = Bar : int -> bar foo | Baz : string -> baz foo
type afoo = A : 'a foo -> afoo
let update_afoo : afoo -> afoo = fun x ->
let (A y) = x in
let z = match y with Bar i -> Bar (i+1) | Baz s -> Baz (s ^ "1") in
A z
This produces the error on Baz (s ^ "1")
that
This expression has type $a foo but an expression was expected of type
bar foo
Type $a is not compatible with type bar
Hint: $a is an existential type bound by the constructor A.
Evidently the problem is that OCaml has inferred from the type of the result Bar (i+1)
of the first branch that the result type of the match
should be the specific bar foo
, whereas I really wanted the type of the match to be the more general $a foo
where $a
is the type of y
. If I had a name for $a
then I could annotate the match so that it would work, but I don’t.
The only real solution I’ve found to this sort of problem is to define a helper function with an explicitly polymorphic type:
et update_afoo : afoo -> afoo = fun x ->
let (A y) = x in
let makez : type a. a foo -> a foo = fun y ->
match y with Bar i -> Bar (i+1) | Baz s -> Baz (s ^ "1") in
A (makez y)
But this is kind of annoying, especially having to write out the whole type of the helper function, which in applications can be quite long as it needs to be given as arguments all the other variables in scope whose type should be refined in the match bodies. Is there a better way to deal with it?