Patterning matching with or-pattern, locally abstract types and GADT has some curious semantics.
Consider the code below , which is a standard implementation of type witness and equality witness, with an additional problematic implementation that simply tries to merge two similar branches of pattern matching into a single branch using an or-pattern. The two parts of the or-pattern: (Int, Int) and (Char, Char) make incompatible refinements for the locally abstract types a and b. The former refines them both to int, whilst the latter — char. The machine inferred type, as shown by the error message, for Refl is (a, a) equal ,which is not compatible with the annotated return type (a, b) equal, therefore the definition is rejected.
The question:
How exactly the type (a, a) equal is inferred for Refl?
My own answer to question 1: Start with the result ('c, 'c) of anti-unification of the tuples (int, int) and (char, char), followed by a conversion from free type variable to abstract type: ('c, 'c) -->> (a, a). But why a? Why not b nor a fresh locally abstract type? Why the unnecessary conversion ('c, 'c) -->> (a, a)?
I am also wondering where can I find from the ocaml source, about code that implements pattern matching and locally abstract types?
module Ty : sig
type _ t = Int : int t | Char : char t
type (_, _) equal = Refl : ('a, 'a) equal
val ty_equal : 'a t -> 'b t -> ('a, 'b) equal option
end = struct
type _ t = | Int : int t
| Char : char t
type (_, _) equal = Refl : ('a, 'a) equal
let ty_equal (type a b) (a : a t) (b : b t) : (a, b) equal option
= match a , b with
| Int , Int -> Some Refl
| Char, Char -> Some Refl
| _, _ -> None
module Problem = struct
let ty_equal (type a b) (a : a t) (b : b t) : (a, b) equal option
= match a , b with
| (Int , Int) | (Char, Char) -> Some Refl
| _, _ -> None
end
end