Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created November 13, 2014 04:20
Show Gist options
  • Select an option

  • Save johnynek/c774c4e18f0e1701fea1 to your computer and use it in GitHub Desktop.

Select an option

Save johnynek/c774c4e18f0e1701fea1 to your computer and use it in GitHub Desktop.

Revisions

  1. johnynek created this gist Nov 13, 2014.
    139 changes: 139 additions & 0 deletions Future.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,139 @@

    use std::comm::{Receiver, channel};
    use std::io;
    use std::mem::replace;
    use std::task::spawn;

    struct Future<'a, A> {
    state: FutureState<'a, A>
    }

    enum FutureState<'a, A> {
    Pending(proc():'a -> A),
    Evaluating, // This state is only here to satify the compiler in get_ref
    Forced(A),
    }

    /*
    * This is based on the rust std::sync::Future
    * https://github.com/rust-lang/rust/blob/master/src/libstd/sync/future.rs
    * with the addition of map and and_then.
    * Note, I am very new to rust and much of this is non-idiomatic (and probably dumb and/or broken, but it runs).
    *
    * This future is only designed to either be asynchronous and evaluate in another task,
    * or lift a literal value into the future.
    *
    * Note this Future is lazy and mapping and and_then procs are called as needed to evaluate
    * get_ref calls. If there are side-effects in those functions, all leaf Futures should have
    * their get_ref fn called for the logic to be correct.
    *
    * For me, it is still unclear where the right parts of the design space are: there are
    * interesting trade-offs between using references and lifetimes vs boxes vs cloning.
    * Here we avoid copying/cloning or GC/ref-counting in map/and_then.
    */
    impl<'a, A> Future<'a, A> {
    /*
    * This is the function to lift something into the future (monad)
    */
    pub fn from(val: A) -> Future<'a, A> {
    Future { state: Forced(val) }
    }
    /*
    * This can be used on non-mutable refs if the future is Forced
    */
    pub fn get_opt<'b>(&'b self) -> Option<&'b A> {
    match self.state {
    Forced(ref a) => return Some(a),
    _ => return None
    }
    }
    pub fn get_ref<'b>(&'b mut self) -> &'b A {
    match self.state {
    Forced(ref a) => return a,
    Pending(_) => {
    /*
    * This is copying the sync::Future since
    * we can't take p and evaluate it in one go, and
    * we can't call p if we are just borrowing self (as far as I can see)
    */
    match replace(&mut self.state, Evaluating) {
    Forced(_) | Evaluating => panic!("Unreachable"),
    Pending(p) => {
    self.state = Forced(p());
    return self.get_ref()
    }
    }
    }
    Evaluating => panic!("Unreachable")
    }
    }
    /*
    * Should this be a proc or a closure? We only call it once.
    */
    pub fn map<B>(&mut self, mapfn: proc(&A) -> B) -> Future<'a, B> {
    match self.state {
    Forced(ref a) => Future { state: Forced(mapfn(a)) },
    Pending(_) => Future {
    state: Pending(proc() {
    mapfn(self.get_ref())
    })
    },
    Evaluating => panic!("Unreachable")
    }
    }

    /*
    * This is the monadic bind function
    *
    * Should this be a proc or a closure? We only call it once.
    */
    pub fn and_then<'b, B>(&mut self, bindfn: proc(&A) -> Future<'b, B>) -> Future<'b, B> {
    match self.state {
    Forced(ref a) => bindfn(a),
    Pending(_) => Future {
    state: Pending(proc() {
    let a = self.get_ref();
    let mut fb = bindfn(a);
    // Force the future, but ignore the reference, use move semantics below
    let _ = fb.get_ref();
    match fb.state {
    Forced(b) => return b,
    _ => panic!("get_ref should have forced us")
    }
    })
    },
    Evaluating => panic!("Unreachable")
    }
    }
    }

    impl<'a, A:Send> Future<'a, A> {
    pub fn async(f: proc(): Send -> A) -> Future<'a, A> {
    let (tx, rx) = channel();
    spawn(proc() {
    tx.send(f())
    });
    Future { state: Pending(proc() { rx.recv() }) }
    }
    }

    fn main() {
    println!("please enter a line:");
    let input = io::stdin().read_line().ok().expect("Failed to read a line");

    println!("You entered: {}", input)
    println!("string had len = {}",
    Future::from(input.clone())
    .map(proc(s) { return s.len() })
    .get_ref()
    )

    println!("string had len (and_then) = {}",
    Future::from(input)
    .and_then(proc(s) {
    let sc = s.clone();
    Future::async(proc() { return sc.len() } )
    })
    .get_ref()
    )
    }