Created
June 15, 2014 21:34
-
-
Save TethysSvensson/d98e36292a3f92c6291e to your computer and use it in GitHub Desktop.
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
#![feature(macro_rules)] | |
use std::cell::{RefCell, Ref}; | |
use std::rc::Rc; | |
pub struct Thunk<T>(Rc<RefCell<ThunkValue<T>>>); | |
pub enum ThunkValue<T> { | |
Eager(T), | |
Lazy(proc() -> T) | |
} | |
pub struct ThunkRef<'b, T>(Ref<'b, ThunkValue<T>>); | |
impl<T> Deref<Rc<RefCell<ThunkValue<T>>>> for Thunk<T> { | |
fn deref<'a>(&'a self) -> &'a Rc<RefCell<ThunkValue<T>>> { | |
let &Thunk(ref inner) = self; | |
inner | |
} | |
} | |
impl<T> Clone for Thunk<T> { | |
fn clone(&self) -> Thunk<T> { | |
Thunk(self.deref().clone()) | |
} | |
} | |
impl<'b, T> Deref<T> for ThunkRef<'b, T> { | |
fn deref<'a>(&'a self) -> &'a T { | |
let &ThunkRef(ref inner) = self; | |
match inner.deref() { | |
&Eager(ref value) => value, | |
&Lazy(_) => unreachable!() | |
} | |
} | |
} | |
impl<T> Thunk<T> { | |
pub fn borrow<'a>(&'a self) -> ThunkRef<'a, T> { | |
/* We assert the following invariants: | |
- Outside of Thunk.borrow(), nobody will hold a RefMut. | |
- Nobody outside Thunk.borrow holds a ThunkRef to a Lazy value. | |
When this is combined with the fact that ThunkRef is NOT Share, we can | |
conclude that the calls to cell.borrow and cell.borrow_mut are safe. | |
*/ | |
let cell: &'a RefCell<ThunkValue<T>> = self.deref().deref(); | |
/* First check if it evalutated */ | |
{ | |
/* Nobody else can hold a RefMut, so this is safe */ | |
let cell = cell.borrow(); | |
match cell.deref() { | |
&Eager(_) => return ThunkRef(cell), | |
&Lazy(_) => () | |
} | |
} | |
/* It was not evaluated. Now evaluate it. */ | |
{ | |
/* Since it was Lazy, nobody else can hold refs at all. */ | |
let mut cell = cell.borrow_mut(); | |
let mutref = cell.deref_mut(); | |
/* Temporarily replace the mutref, so we can move the proc out */ | |
let orig = std::mem::replace(mutref, Lazy(proc() { fail!() })); | |
let f = match orig { | |
Eager(_) => unreachable!(), /* We /just/ checked this. */ | |
Lazy(f) => f | |
}; | |
let val = f(); | |
*mutref = Eager(val); | |
} | |
/* Now that it has been evaluated, we return it. */ | |
ThunkRef(cell.borrow()) | |
} | |
} | |
/* Convenience functions */ | |
pub fn thunk<T>(t: ThunkValue<T>) -> Thunk<T> { | |
Thunk(Rc::new(RefCell::new(t))) | |
} | |
pub fn eager<T>(t: T) -> Thunk<T> { | |
thunk(Eager(t)) | |
} | |
/* fn lazy!(Expression<T>) -> Thunk<T> */ | |
macro_rules! lazy( | |
($e:expr) => (thunk(Lazy(proc() { $e }))) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment