Applications of GADTs include
generic programming, modelling programming languages (
higher-order abstract syntax), maintaining
invariants in
data structures, expressing constraints in
embedded domain-specific languages, and modelling objects.
Higher-order abstract syntax An important application of GADTs is to embed
higher-order abstract syntax in a
type safe fashion. Here is an embedding of the
simply typed lambda calculus with an arbitrary collection of base types,
product types (
tuples) and a
fixed point combinator: data Lam :: * -> * where Lift :: a -> Lam a -- ^ lifted value Pair :: Lam a -> Lam b -> Lam (a, b) -- ^ product Lam :: (Lam a -> Lam b) -> Lam (a -> b) -- ^ lambda abstraction App :: Lam (a -> b) -> Lam a -> Lam b -- ^ function application Fix :: Lam (a -> a) -> Lam a -- ^ fixed point And a type safe evaluation function: eval :: Lam t -> t eval (Lift v) = v eval (Pair l r) = (eval l, eval r) eval (Lam f) = \x -> eval (f (Lift x)) eval (App f x) = (eval f) (eval x) eval (Fix f) = (eval f) (eval (Fix f)) The factorial function can now be written as: fact = Fix (Lam (\f -> Lam (\y -> Lift (if eval y == 0 then 1 else eval y * (eval f) (eval y - 1))))) eval(fact)(10) Problems would have occurred using regular algebraic data types. Dropping the type parameter would have made the lifted base types existentially quantified, making it impossible to write the evaluator. With a type parameter, it is still restricted to one base type. Further, ill-formed expressions such as App (Lam (\x -> Lam (\y -> App x y))) (Lift True) would have been possible to construct, while they are type incorrect using the GADT. A well-formed analogue is App (Lam (\x -> Lam (\y -> App x y))) (Lift (\z -> True)). This is because the type of x is Lam (a -> b), inferred from the type of the Lam data constructor. ==See also==