Skip to content

Instantly share code, notes, and snippets.

@TethysSvensson
Created June 15, 2014 21:34
Show Gist options
  • Save TethysSvensson/d98e36292a3f92c6291e to your computer and use it in GitHub Desktop.
Save TethysSvensson/d98e36292a3f92c6291e to your computer and use it in GitHub Desktop.
#![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