gdritter repos rust-examples / 0c4be8e
Updated lcalc to newest nightly Getty Ritter 9 years ago
1 changed file(s) with 39 addition(s) and 36 deletion(s). Collapse all Expand all
1 use std::task;
1 // This isn't a very Rust-ey lambda-calculus implementation. It's much
2 // more TAPL-ey, which I think makes it nice for pedagogical purposes
3 // when aimed at functional programmers, but you wouldn't actually
4 // use Rust like this in practice.
25
3 #[deriving(Eq,PartialEq,Clone,Show,Send)]
6 #[deriving(Eq,PartialEq,Clone,Show)]
47 enum Term {
58 Num(int),
69 Var(String),
1215 // The following are wrappers over λ-terms to simplify writing
1316 // allocations. It really does help, as you can see in main.
1417 fn num(n: int) -> Box<Term> {
15 box Num(n)
18 box Term::Num(n)
1619 }
1720
1821 fn var(s: &str) -> Box<Term> {
19 box Var(s.to_string())
22 box Term::Var(s.to_string())
2023 }
2124
2225 fn lam(x: &str, n: Box<Term>) -> Box<Term> {
23 box Lam(x.to_string(), n)
26 box Term::Lam(x.to_string(), n)
2427 }
2528
2629 fn app(x: Box<Term>, y: Box<Term>) -> Box<Term> {
27 box App(x, y)
30 box Term::App(x, y)
2831 }
2932
3033 fn let_(x: &str, y: Box<Term>, z: Box<Term>) -> Box<Term> {
31 box Let(x.to_string(), y, z)
34 box Term::Let(x.to_string(), y, z)
3235 }
3336
3437 // A value is either a number or a closure, which has to have
3538 // its environment around. We'll have to clone the environment
3639 // into the closure to make sure that it stays around even if
3740 // the closure is returned from the environment where it was used.
38 #[deriving(Eq,PartialEq,Clone,Show,Send)]
41 #[deriving(Eq,PartialEq,Clone,Show)]
3942 enum Val {
40 VNum(int),
41 VLam(String, Box<Term>, Box<Env>),
43 Num(int),
44 Lam(String, Box<Term>, Box<Env>),
4245 }
4346
4447 // I could also use a pair of a map and a parent pointer, but
4548 // this is a little more TAPL-ish. Plus, we generally always
4649 // bind a single variable at a time.
47 #[deriving(Eq,PartialEq,Clone,Show,Send)]
50 #[deriving(Eq,PartialEq,Clone,Show)]
4851 enum Env {
4952 Empty,
5053 Binding(String, Box<Val>, Box<Env>),
5457 // We can always wrap this in another thread if we want to get a value.
5558 fn lookup(s: &String, e: &Env) -> Box<Val> {
5659 match *e {
57 Empty => { fail!(format!("Couldn't find {} in environment", s)) }
58 Binding(ref n, ref v, ref p) => {
60 Env::Empty => { panic!(format!("Couldn't find {} in environment", s)) }
61 Env::Binding(ref n, ref v, ref p) => {
5962 if n == s {
6063 v.clone()
6164 } else {
7073 // ownership.
7174 fn lcalc_eval(t: &Term, e: &Env) -> Box<Val> {
7275 match t {
73 &Num(num) => {
74 box VNum(num)
76 &Term::Num(num) => {
77 box Val::Num(num)
7578 }
76 &Var(ref str) => {
79 &Term::Var(ref str) => {
7780 lookup(str, e)
7881 }
79 &Lam(ref s, ref b) => {
80 box VLam(s.clone(), b.clone(), box e.clone())
82 &Term::Lam(ref s, ref b) => {
83 box Val::Lam(s.clone(), b.clone(), box e.clone())
8184 }
82 &App(box ref f, box ref x) => {
85 &Term::App(box ref f, box ref x) => {
8386 match *lcalc_eval(f, e) {
84 VLam(ref arg, box ref body, box ref env) => {
85 let newEnv = Binding(arg.clone(),
86 lcalc_eval(x, e),
87 box env.clone());
88 lcalc_eval(body, &newEnv)
87 Val::Lam(ref arg, box ref body, box ref env) => {
88 let new_env = Env::Binding(arg.clone(),
89 lcalc_eval(x, e),
90 box env.clone());
91 lcalc_eval(body, &new_env)
8992 }
90 _ => fail!("Tried to apply a non-function!")
93 _ => panic!("Tried to apply a non-function!")
9194 }
9295 }
93 &Let(ref s, box ref t, box ref b) => {
94 let newEnv = Binding(s.clone(),
95 lcalc_eval(t, e),
96 box e.clone());
97 lcalc_eval(b, &newEnv)
96 &Term::Let(ref s, box ref t, box ref b) => {
97 let new_env =
98 Env::Binding(s.clone(),
99 lcalc_eval(t, e),
100 box e.clone());
101 lcalc_eval(b, &new_env)
98102 }
99103 }
100104 }
102106 // This copies the arguments and evaluates it in another thread, returning
103107 // None if the evaluation fails.
104108 fn lcalc_eval_opt(t: &Term, e: &Env) -> Option<Box<Val>> {
105 let (tx, rx) = channel();
106109 let new_term = t.clone();
107110 let new_env = e.clone();
108 let result = task::try(proc() {
109 tx.send(lcalc_eval(&new_term, &new_env));
111 let guard = std::thread::Thread::spawn(move || {
112 lcalc_eval(&new_term, &new_env)
110113 });
111 match result {
112 Ok(_) => Some(rx.recv()),
114 match guard.join() {
115 Ok(x) => Some(x),
113116 Err(_) => None,
114117 }
115118 }
129132 let s3 = app(lam("x", var("y")), num(5));
130133 // (2)(3), which will also obviously fail
131134 let s4 = app(num(2), num(3));
132 let e = Empty;
135 let e = Env::Empty;
133136 println!("s1: {:}", lcalc_eval_opt(&*s1, &e));
134137 println!("s2: {:}", lcalc_eval_opt(&*s2, &e));
135138 println!("s3: {:}", lcalc_eval_opt(&*s3, &e));