monad 0.0.2

Stackless monads in Rust
#![feature(phase)]
#![feature(unboxed_closures)]

#[phase(link, plugin)]
extern crate monad_macros;
extern crate monad;

use std::collections::BTreeMap;

use monad::monad::reader::trampoline::{
    Reader,
    ask,
    bind,
    point,
};

use self::Exp::{
    Add,
    Let,
    Val,
    Var,
};

#[deriving(Clone)]
#[deriving(Show)]
enum Exp
{
    Add(Box<Exp>, Box<Exp>),
    Let(String, Box<Exp>, Box<Exp>),
    Val(uint),
    Var(String),
}

#[allow(dead_code)]
fn eval<'a>(e: Exp) -> Reader<'a, BTreeMap<String, uint>, Option<uint>> {
    match e {
        Add(box e1, box e2) => {
            mdo! {
                o1: Option<uint> <- eval(e1);
                o2: Option<uint> <- eval(e2);
                let res =
                    o1.and_then(|v1|
                    o2.and_then(|v2|
                        Some(v1 + v2)
                    ));
                end point(res)
            }
        },
        Let(x, box e1, box e2) => {
            mdo! {
                o1: Option<uint> <- eval(e1);
                end eval(e2).local(move |:mut ctx: BTreeMap<String, uint>| {
                    o1.map(|v1| (&mut ctx).insert(x.clone(), v1));
                    ctx
                })
            }
        },
        Val(n) => point(Some(n)),
        Var(x) => {
            mdo! {
                ctx: BTreeMap<String, uint> <- ask();
                end point(ctx.get(&x).map(|x| *x))
            }
        },
    }
}

#[allow(dead_code)]
fn main() {
    fn exp_test(var: &str) -> Exp {
        Let(String::from_str("x"),
            box Val(42u),
            box Add(
                box Let(String::from_str("y"),
                    box Val(43u),
                    box Add(
                        box Var(String::from_str("x")),
                        box Var(String::from_str("y"))
                    )
                ),
                box Var(String::from_str(var))
            )
        )
    }

    println!("{}", eval(exp_test("x")).run(BTreeMap::new()));
    println!("{}", eval(exp_test("y")).run(BTreeMap::new()));
}