Lecture 16 (2015-11-02)
Recursive types; midterm debriefing
**Sum types**
We first covered sum types, which are the "math" version of Haskell's
`Either` datatype.
```
t ::= ... | t1 + t2
e ::= ... | left e | right e | case e of left x -> e1 | right y -> e2
G |- e : t1
---------------------
G |- left e : t1 + t2
G |- e : t2
---------------------
G |- right e : t1 + t2
G |- e : t1 + t2
G,x:t1 |- e1 : t1
G,y:t2 |- e2 : t2
-------------------------
G |- case e of : t
left x -> e1
right y -> e2
```
The above rules are "extrinsic" typing rules, since `left` and `right`
end up guessing their arguments. We could write subscripted versions
(using `_`), like below, to come up with intrinsic forms:
```
t ::= ... | t1 + t2
e ::= ... | left_t2 e | right_t1 e | ...
G |- e : t1
---------------------
G |- left_t2 e : t1 + t2
G |- e : t2
---------------------
G |- right_t1 e : t1 + t2
```
Note how the left and right constructors are annotated with the type
of the *other* side: `left_t2` says what type the programmer thinks
the right side should have.
What values have type `t1 + t2`? Constructors make values; we'll say
that a constructor applied to a value is a value.
```
v ::= ... | left v | right v
---------------------------- CaseLeft
case left v of --> e1[v/x]
left x -> e1
right y -> e2
---------------------------- CaseLeft
case right v of --> e2[v/y]
left x -> e1
right y -> e2
```
We can also give big-step rules, using environments:
```
e, env VV left v e1,env[v/x] VV v'
------------------------------------- CaseLeft
case e of , env VV v'
left x -> e1
right y -> e2
e, env VV left v e2,env[v/y] VV v'
------------------------------------- CaseRight
case e of , env VV v'
left x -> e1
right y -> e2
```
**Recursive types**
Recursion in Haskell functions and Haskell datatypes uses
*names*. Recursion in the pure lambda calculus uses the Y combinator
to write "closed-form" recursive functions. We use μ types to write
closed-form recursive datatypes.
Note that `fold_{mu alpha. t}` is meant to be interpreted as a
subscript.
```
t ::= ... | mu alpha. t | alpha
e ::= ... | fold_{mu alpha. t} e | unfold e
G |- e : t[mu alpha. t/a]
---------------------------------------
G |- fold_{mu alpha. t} e : mu alpha. t
G |- e : mu alpha. t
--------------------------------
G |- unfold e : t[mu alpha. t/a]
```
These definitions are hard to understand at first blush. Let's look at
the definition for integer lists in Haskell and work our way down:
```
data IntList =
Nil
| Cons Int IntList
```
An `IntList` is either:
- `Nil`, or
- `Cons n l`, such that `n` is an `Int` and `l` is an `IntList`.
In algebraic types, we interpret 'either' using sums (written + in
math and `Either` in Haskell). So in pseudo-Haskell, we have:
```
IntList = Nil + Cons Int IntList
```
Next, we don't use constructor names in algebraic datatypes---we just
represent the data. So `Nil` and `Cons` need to be replaced. Now,
`Nil` has no arguments---the only information `Nil` provides is that
it isn't `Cons`. We can represent it using `()`, the unit type.
```
IntList = () + Cons Int IntList
```
`Cons`, on the other hand, has some arguments. We'll represent them as
a pair, using * (or × on paper, or `(,)` in Haskell).
```
IntList = () + (Int,IntList)
```
At this point, the only left to do is to get rid of the recursion. To
do this, we use μ. The process is mechanical: if you have a
definition of the form `X = [some type t in terms of X]`, you
translate it to `X` = μ
α. t[^{α}/_{X}]. Here, that gives us:
```
X = mu alpha. () + (Int,alpha)
```
Okay: what values have μ types?
```
v ::= ... | fold_{mu alpha. t} v
-----------------------------------
unfold (fold_{mu alpha. t} v) --> v
```
We can also give rules in a big-step style:
```
e, env VV fold_{mu alpha. t} v
------------------------------
unfold e, env VV v
```
Hmm... still not helping. How do we build a value with folds? Let's
use the IntList above to build some lists.
```
IntList = mu alpha. () + (Int,alpha)
nil :: IntList
nil = fold_IntList (left ())
```
Okay... what about a list with elements? I'll just write `fold` without
its subscript, to keep things brief.
```
[1,2,3] = fold (right (1,fold (right (2,fold (right (3,fold (left ())))))))
```
Okay... how do we define `cons`?
```
cons :: Int -> IntList -> IntList
cons = \x. \xs. fold (right (x,xs))
```
What about `head` and `tail`?
```
head :: IntList -> Int
head xs =
case unfold xs of
left x -> diverge
right y -> fst y
tail :: IntList -> IntList
tail xs =
case unfold xs of
left x -> diverge
right y -> snd y
```
What about `map`?
```
map :: (Int -> Int) -> IntList -> IntList
map f xs =
case unfold xs of
left e -> nil
right c -> cons (f (head c)) (map f (tail c))
```
**Midterms**
Midterms were handed back at the end of class. If you don't have
yours, I'll bring it by on Wednesday.
Please check my arithmetic and data entry on Sakai!