Skip to content

Instantly share code, notes, and snippets.

@johnynek
Created November 13, 2014 04:20
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnynek/c774c4e18f0e1701fea1 to your computer and use it in GitHub Desktop.
Save johnynek/c774c4e18f0e1701fea1 to your computer and use it in GitHub Desktop.
Future with map and monadic bind in Rust.
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()
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment