# Generalised algebraic datatype

(Difference between revisions)
 Revision as of 09:27, 2 May 2006 (edit)m (:@ constructor symbol -- infix also in data declaration)← Previous diff Revision as of 10:58, 2 May 2006 (edit) (undo) (Adding link to Existential type at the example where this concept is mentioned)Next diff → Line 50: Line 50: In the definition of foo': foo "S" = S In the definition of foo': foo "S" = S - One could, however, reason that parse has type: String -> exists a. Term a + One could, however, reason that parse has type: String -> exists a. Term a, see also [[Existential type]]. == Example with lists == == Example with lists ==

## 1 Motivating example

Generalised Algebraic Datatypes (GADTs) are datatypes for which a constructor has a non standard type. Indeed, in type systems incorporating GADTs, there are very few restrictions on the type that the data constructors can take. To show you how this could be useful, we will implement an evaluator for the typed SK calculus. Note that the K combinator is operationally similar to $\lambda\;x\;y\;.\;x$ and, similarly, S is similar to the combinator $\lambda\;x\;y\;z\;.\;x\;z\;(\;y\;z\;)$ which, in simply typed lambda calculus, have types $a \rightarrow b \rightarrow a$ and $(a \rightarrow b \rightarrow c) \rightarrow (a \rightarrow b) \rightarrow a \rightarrow c$ Without GADTs we would have to write something like this:

data Term = K | S | Term :@ Term
infixl 6 :@

With GADTs, however, we can have the terms carry around more type information and create more interesting terms, like so:

data Term x where
K :: Term (a -> b -> a)
S :: Term ((a -> b -> c)  -> (a -> b) -> a -> c)
Const :: a -> Term a
(:@) :: Term (a -> b) -> (Term a) -> Term b
infixl 6 :@

now we can write a small step evaluator:

eval::Term a -> Term a
eval (K :@ x :@ y) = x
eval (S :@ x :@ y :@ z) = x :@ z :@ (y :@ z)
eval x = x

Since the types of the so-called object language, being the typed SK calculus, are mimicked by the type system in our meta language, being haskell, we have a pretty convincing argument that the evaluator won't mangle our types. We say that typing is preserved under evaluation (preservation.) Note that this is an argument and not a proof.

This, however, comes at a price: let's see what happens when you try to convert strings into our object language:

parse "K" = K
parse "S" = S

you'll get a nasty error like so:

   Occurs check: cannot construct the infinite type: c = b -> c
Expected type: Term ((a -> b -> c) -> (a -> b) -> a -> b -> c)
Inferred type: Term ((a -> b -> c) -> (a -> b) -> a -> c)
In the definition of foo': foo "S" = S

One could, however, reason that parse has type:
String -> exists a. Term a

## 2 Example with lists

here's another, smaller example:

data Empty
data NonEmpty
data List x y where
Nil :: List a Empty
Cons:: a -> List a b ->  List a NonEmpty

safeHead:: List x NonEmpty -> x
safeHead (Cons a b) = a

now safeHead can only be applied to non empty lists, and will never evaluate to bottom. This too comes at a cost; consider the function:

silly 0 = Nil
silly 1 = Cons 1 Nil

yields an objection from ghc:

Couldn't match Empty' against NonEmpty'
Expected type: List a Empty
Inferred type: List a NonEmpty
In the application Cons 1 Nil'
In the definition of silly': silly 1 = Cons 1 Nil
`