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.