## Simple and not-so-simple examples of GADTs and DataKinds

`{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}`

Kinds describe types, just like types describe terms. For example, the parameter to `T`

below must have the kind of parameterized types, like `Maybe`

or `[]`

.

`data T (a :: * -> *) = MkT (a Int)`

Datakinds means that we can use datatypes as kinds. This kind is parameterized by a boolean flag.

`data U (a :: Bool) = MkU`

What about a version of lists where the flag indicates whether the list is empty or not. We can design a special purpose flag:

`data Flag = Empty | NonEmpty`

And then use it in the definition of lists. However, normal datatype definitions don't have the semantics that we want.

`-- data List (f :: Flag) (a :: *) = Nil | Cons a (List f a)`

For example, `Nil`

has type `List f a`

for any `f`

, including `NonEmpty`

.

GADTs on the otherhand, allow the result type of data constructors to vary. In this case, we can give `Nil`

a type that statically declares that the list is empty. Analogously, the type of `Cons`

too reflects that it creates a non-empty list. Note that the second argument of `Cons`

could have either flag---it could be an empty or non-empty list.

```
data List :: Flag -> * -> * where
Nil :: List Empty a
Cons :: a -> List f a -> List NonEmpty a
```

```
y :: List Empty Int
y = Nil
```

```
x :: List NonEmpty Int
x = Cons 1 (Cons 2 (Cons 3 Nil))
```

The head function can require that its argument be a non-empty list. If we give it an empty list, then it will be a type error. Even writing case for `Nil`

in the patterns for `safeHd`

produces an error.

```
safeHd :: List NonEmpty a -> a
safeHd (Cons x _) = x
-- safeHd Nil = error "there's no head of an empty list"
```

This flag doesn't interact much with some list functions. `foldr`

works for both empty and non-empty lists.

```
foldr' :: (a -> b -> b) -> b -> List f a -> b
foldr' f b Nil = b
foldr' f b (Cons x xs) = f x (foldr' f b xs)
```

But the foldr1 variant can require that its argument be nonempty. Note that in the second patter we have to explicitly match against `Cons`

because the type checker does not observe the order of the patterns. It doesn't know that xs must be `Cons`

at this point (which is required by the recursive call).

```
foldr1' :: (a -> a -> a) -> List NonEmpty a -> a
foldr1' _ (Cons x Nil) = x
foldr1' f (Cons x xs@(Cons _ _)) = f x (foldr1' f xs)
```

The type of `map`

is really strong now. It says that we must take empty lists to empty lists and non-empty lists to non-empty lists. If we forgot the `Cons`

in the last line, the function wouldn't type check. (Though it would type check if we did two `Cons`

.)

```
map' :: (a -> b) -> List f a -> List f b
map' g Nil = Nil
map' g (Cons x xs) = Cons (g x) (map' g xs)
```

There's no good type for `filter`

though. We don't know whether the output list will be empty or non-empty. It may not match the input list (for example, if all elements are filtered out of a nonempty list).

So this type doesn't work.

`-- filter' :: (a -> Bool) -> List f a -> List f a `

This type also doesn't work.

`-- filter' :: (a -> Bool) -> List f a -> List f' a`

Above, `f'`

is general, the type says that filter will work for *any* `f'`

. But that is not true, it will work for only one `f'`

, we just don't know what it is.

The only solution is to hide that flag in an auxiliary datatype.

```
data OldList a where
OL :: List f a -> OldList a
```

And use that as the result of filter. But that requires some additional pattern matching.

```
filter' :: (a -> Bool) -> List f a -> OldList a
filter' g Nil = OL Nil
filter' g (Cons x xs) = case filter' g xs of
OL l -> if g x then OL (Cons x l) else OL l
```

## News :

Welcome to CIS 552!

See the home page for basic
information about the course, the schedule for the lecture notes
and assignments, the resources for links to the required software
and online references, and the syllabus for detailed information about
the course policies.