15-312 Recitation #7: Recursive Types
2002-10-09
Joshua Dunfield (joshuad@cs)
Carnegie Mellon University
- Trees
- Using recursive types to simulate recursive functions
- [asst4] Questions
Announcements:
- graded asst3's will be handed back tomorrow
- midterm on 17th
TREES
datatype tree = E (* empty *)
| B of int * tree * tree (* branch *)
1. write using mu, +
tree = mu a. 1 + (int * (a * a))
2. write "constructors" e, b like E, B
let e = roll(inl()) (* e : mu a. 1 + ... *)
let b = \x.\l.\r. roll(inr(x, (l, r)))
(* b : int -> tree -> tree -> (mu a. 1 + (int * (a * a))) *)
3. write destructor "tcase" like case in SML
(* tcase : All r. tree -> (unit -> r) -> (int * (tree * tree) -> r) -> r *)
let tcase =
Fun r. \t. \f1. \f2.
case unroll(t) of
inl(_) => f1 ()
| inr(p) => f2 (p)
SIMULATING RECURSIVE FUNCTIONS WITH RECURSIVE TYPES
With `fun', it's easy to write a nonterminating MinML program:
(fun f (x:int):int in f x end) 0;
But we can't do this with lambdas.
Can we simulate this using just lambda and mu?
Hint: The term (\x. x x) (\x. x x) reduces to itself, but is ill-typed.
However, it provides a pattern...
First part of the solution:
Define a type
w = mu a. a -> a
Now, the judgment
x:w |- unroll(x) : w -> w
can be derived, as well as:
x:w |- unroll(x) x : w
Hmm, this looks awfully like the premise
for the typing rule for lambda.
|- (\x. unroll(x) x) : w -> w
But wait, that has the form a -> a...
|- roll(\x. unroll(x) x) : w
Let's call (\x. unroll(x) x) "Omega". So what we have to play with are
Omega : w -> w
and
roll(Omega) : w
So:
|- Omega (roll(Omega)) : w
What happens when we evaluate this? The transition rules for unroll
roll are very straightforward:
---------------------
unroll(roll(v)) |-> v
e |-> e' e |-> e'
------------------------ --------------------
unroll(e) |-> unroll(e') roll(e) |-> roll(e')
and we have
e1 |-> e1' e2 |-> e2'
------------------ ------------------ -------------------
e1(e2) |-> e1'(e2) v1(e2) |-> v1(e2') (\x.e)v |-> {v/x} e
So...
Omega (roll(Omega)) |-> {roll(Omega) / x} unroll(x) x
= unroll(roll(Omega)) (roll(Omega))
|-> Omega (roll(Omega))
|-> {roll(Omega) / x} unroll(x) x
= unroll(roll(Omega)) (roll(Omega))
|-> Omega (roll(Omega))
and so on.
By making the w type polymorphic, we can extend this to encode any
recursive function, such as
fun fact (x:int): int is if x=0 then 1 else x * fact(x-1) fi end
The polymorphic w is
w = All t. mu a. a -> t
Now we encode `fact' by adding an argument `f' and replacing every
recursive invocation with
(unroll(f) f)
like this:
let F = \f.\x. if x=0 then 1 else x * (unroll(f) f) (x-1) fi
One more step:
let fact = F (roll F)
Now
fact 5 = 120
EXAMPLES
--------
Encodings of the above into Standard ML can be found in the recitations
directory as recursive_types.sml.