Say I want to create a GADT t for terms, the latter being of 3 different sorts (say a, e and f). Now is there a way to state than one constructor may only accept f or es? I know how to do this with two different constructors (see Ite_f and Ite_e below) but I wondered if there is another good practice to handle this kind of situations (from what I gathered, using polymorphic variants for f and eand then playing with subtyping is discouraged when using GADTs).
type e (* expressions *)
type f (* formulas *)
type a (* arithmetic terms *)
type 'a t =
Num : int -> a t
| Sort : string -> e t
| True : f t
| False : f t
| All : string * e t * f t -> f t
| Ite_f : f t * f t * f t -> f t (* can I get a *single* Ite constructor *)
| Ite_e : f t * e t * e t -> e t
type _ e_or_f =
| E : e e_or_f
| F : f e_or_f
type _ t =
...
| If : 'a e_or_f * f t * 'a t * 'a t -> 'a t
Which is structurally very similar to having two If constructors, of course.
Expressing arbitrary refinements in this way can get quite ugly and mechanism heavy, so be aware that you might be in for some work if the design has to change.
Indeed this is isomorphic to my first example.
As a matter of fact, I’d prefer this:
type a = [ `A ]
type e = [ `E ]
type f = [ `F ]
type 'a t =
Num : int -> a t
| Sort : string -> e t
| True : f t
| False : f t
| All : string * e t * f t -> f t
| Ite : f t * 'a t * 'a t -> ([< `E | `F ] as 'a) t
Then:
utop # Ite (True, True, True);;
- : f t = Ite (True, True, True)
utop # Ite (True, Sort "s", Sort "t");;
- : e t = Ite (True, Sort "s", Sort "t")
utop # Ite (True, True, Sort "s");;
Error: This expression has type e t but an expression was expected of type f t
Type e = [ `E ] is not compatible with type f = [ `F ]
These two variant types have no intersection
utop # Ite (True, True, Num 5);;
Error: This expression has type a t but an expression was expected of type f t
Type a = [ `A ] is not compatible with type f = [ `F ]
These two variant types have no intersection
utop # Ite (True, Num 5, Num 3);;
Error: This expression has type a t but an expression was expected of type
([< `E | `F ] as 'a) t
Type a = [ `A ] is not compatible with type [< `E | `F ] as 'a
These two variant types have no intersection
But I seem to remember that using polymorphic variants and subtyping inside GADTs is not recommended at all. Or is it?