SynopsisThis page gives information on the original implementation of GADTs in OCaml, now merged in trunk. svn checkout http://caml.inria.fr/svn/ocaml/trunk Examples are in the subdirectory testsuite/tests/typing-gadts which you can access directly at the following URL http://caml.inria.fr/cgi-bin/viewvc.cgi/ocaml/trunk/testsuite/tests/typing-gadts/ Status Fully implemented but still needs more testing. Please test and find bugs or ask questions. The -principal flag needs the most testing. Works with both camlp4 and camlp5. About the implementationThe two following documents give some insights on the implementation and its theoretical background.
Important note: the above documents are about the previous version of GADTs. On 2011-11-24, a new version was merged in trunk, with a different approach to ambiguity. In particular the new approach is compatible with objects and polymorphic variants. Example code
IntuitionTo gain some intuition, we must first understand what I refer to as a newtype. In the expression:
the type s is a newtype, and is treated like a type constructor in e. The type s can take on other types, but only within a branch. For example, in the expression:
then the newtype s will have type int in the branch IntLit x -> x and bool in the branch BoolLit x -> x Constructor level existential typesIn the Exp example, we could define a type:
Syntax extensionsSummary
Type parametersThe following declaration is now legal:
and is equivalent to
if 'a and 'b are not in u. For example:
is the same as
Furthermore, unnamed arguments can have a variance marker
ConstructorsConstructors can now be given a more general type: type _ t = | IntLit : int -> int t | BoolLit : bool -> bool t You can also mix and match with normal constructors: type 'a t = | IntLit : int -> int t | BoolLit : bool -> bool t | Lit of 'a Mixing polymorphic recursion and newtypesConsider the evaluator:
We can write an evaluator as follows:
However, this can now be written in the simpler form:
or, even more simply:
You can also quantify multiple arguments:
To see why this extension is necessary, consider the following example:
Here we try to call f with an argument of new type s. If we do not know that f is polymorphic in its argument, we get a type error. Exhaustiveness checkExhaustiveness check is done, to a great extent, by trying many possibilities. For example:
would pass the exhaustiveness check. However:
would not, even though it is impossible to construct a value with the Impossible constructor. Sometimes, there isn't enough information to tell two types apart and this can effect exhaustiveness checking. For example:
would fail the exhaustiveness check because the compiler does not know that M.u can never be equal to M.t. Unused match casesThere are two cases for unused matches: Duplicate variantsThe following unused match case is caught:
Redundant underscoreUnfortunately, the following unused match case is _not_ caught:
VarianceType constructors with GADT data constructors can be variant in all their type parameters which cannot introduce new equations. For example, the following is correct: type (_,+_) foo = Bar : 'a -> (int,'a) foo but the following isn't: type (+_,_) foo = Bar : 'a -> (int,'a) foo JustificationTo see why this is the case, consider the following example (taken from Jacques Garrigue):
LimitationsPolymorphic variantsPatterns containing polymorphic variant patterns are not handled as GADTs. For example, the following code is not allowed:
Generation orderWithin a pattern, constraints are generated, and can only be used, from left to right in tuples and in label definition order for records. For example: let check : type s . s t * s -> bool = function | BoolLit true, false -> false | IntLit 5, 6 -> false would type but the function: let check' : type s . s * s t -> bool = function | true, BoolLit false -> false | 5, IntLit 6 -> false would not. The reason is that patterns are checked in this order during runtime so that check' might compare integers with booleans. Note: for record patterns, left to right must be understood according to the definition order. PropagationOCaml currently does not always propagate the type information. This means that the user will occasionally have to annotate his types. Currently, this code doesn't compile:
|