Created
November 13, 2014 04:20
-
-
Save johnynek/c774c4e18f0e1701fea1 to your computer and use it in GitHub Desktop.
Future with map and monadic bind in Rust.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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