lazy_st/
lib.rs

1#![no_std]
2
3//! Single-threaded lazy evaluation.
4//!
5//! Lazy evaluation allows you to define computations whose
6//! evaluation is deferred to when they are actually needed.
7//! This can be also achieved with closures; however,
8//! in case of lazy evaluation, the output of computations is
9//! calculated only once and stored in a cache.
10//!
11//! Lazy evaluation is useful if you have an expensive computation
12//! of which you might need the result more than once during runtime,
13//! but you do not know in advance whether you will need it at all.
14//!
15//! Let us consider an example, where we first use a closure to defer evaluation:
16//!
17//! ~~~
18//! fn expensive() -> i32 {
19//!     println!("I am expensive to evaluate!"); 7
20//! }
21//!
22//! fn main() {
23//!     let a = || expensive(); // Nothing is printed.
24//!
25//!     assert_eq!(a(), 7); // "I am expensive to evaluate!" is printed here
26//!
27//!     let b = [a(), a()]; // "I am expensive to evaluate!" is printed twice
28//!     assert_eq!(b, [7, 7]);
29//! }
30//! ~~~
31//!
32//! Contrast this with using lazy evaluation:
33//!
34//! ~~~
35//! # use lazy_st::lazy;
36//! fn expensive() -> i32 {
37//!     println!("I am expensive to evaluate!"); 7
38//! }
39//!
40//! fn main() {
41//!     let a = lazy!(expensive()); // Nothing is printed.
42//!
43//!     // Thunks are just smart pointers!
44//!     assert_eq!(*a, 7); // "I am expensive to evaluate!" is printed here
45//!
46//!     let b = [*a, *a]; // Nothing is printed.
47//!     assert_eq!(b, [7, 7]);
48//! }
49//! ~~~
50//!
51//! Lazy values from this crate cannot be shared between threads.
52//! If you need this, please consider using the `lazy-mt` crate.
53
54extern crate alloc;
55
56use alloc::boxed::Box;
57use core::cell::UnsafeCell;
58use core::ops::{Deref, DerefMut};
59
60use self::Inner::{Evaluating, Unevaluated, Value};
61
62/// A lazily evaluated value.
63#[derive(Debug)]
64pub struct Thunk<E, V>(UnsafeCell<Inner<E, V>>);
65
66/// A lazily evaluated value produced from a closure.
67pub type Lazy<T> = Thunk<Box<dyn FnOnce() -> T>, T>;
68
69/// Construct a lazily evaluated value using a closure.
70///
71/// ~~~
72/// # use lazy_st::lazy;
73/// let val = lazy!(7);
74/// assert_eq!(*val, 7);
75/// ~~~
76#[macro_export]
77macro_rules! lazy {
78    ($e:expr) => {
79        $crate::Thunk::new(Box::new(move || $e))
80    };
81}
82
83impl<E, V> Thunk<E, V>
84where
85    E: Evaluate<V>,
86{
87    /// Create a lazily evaluated value from
88    /// a value implementing the `Evaluate` trait.
89    ///
90    /// The `lazy!` macro is preferred if you want to
91    /// construct values from closures.
92    ///
93    /// ~~~
94    /// # use lazy_st::Thunk;
95    /// let expensive = Thunk::new(|| { println!("Evaluated!"); 7 });
96    /// assert_eq!(*expensive, 7); // "Evaluated!" gets printed here.
97    /// assert_eq!(*expensive, 7); // Nothing printed.
98    /// ~~~
99    pub fn new(e: E) -> Thunk<E, V> {
100        Thunk(UnsafeCell::new(Unevaluated(e)))
101    }
102
103    /// Create a new, evaluated, thunk from a value.
104    ///
105    /// ~~~
106    /// # use lazy_st::{Thunk, Lazy};
107    /// let x: Lazy<u32> = Thunk::evaluated(10);
108    /// assert_eq!(*x, 10);
109    /// ~~~
110    pub fn evaluated(v: V) -> Thunk<E, V> {
111        Thunk(UnsafeCell::new(Value(v)))
112    }
113
114    /// Force evaluation of a thunk.
115    pub fn force(&self) {
116        match unsafe { &*self.0.get() } {
117            Value(_) => return,
118            Evaluating => panic!("Thunk::force called during evaluation."),
119            Unevaluated(_) => (),
120        }
121        unsafe {
122            match core::ptr::replace(self.0.get(), Evaluating) {
123                Unevaluated(e) => *self.0.get() = Value(e.evaluate()),
124                _ => unreachable!(),
125            };
126        }
127    }
128
129    /// Force the evaluation of a thunk and get the value, consuming the thunk.
130    ///
131    /// ~~~
132    /// # use lazy_st::lazy;
133    /// let val = lazy!(7);
134    /// assert_eq!(val.unwrap(), 7);
135    /// ~~~
136    pub fn unwrap(self) -> V {
137        self.force();
138        match self.0.into_inner() {
139            Value(v) => v,
140            _ => unreachable!(),
141        }
142    }
143}
144
145/// Generalisation of lazy evaluation to other types than closures.
146///
147/// The main use case for implementing this trait is the following:
148/// Let us suppose that you construct a large number of lazy values using
149/// only one function `f` with different values `x1`, ..., `xn` of type `T`,
150/// i.e. `lazy!(f(x1))`, ..., `lazy!(f(xn))`.
151/// In this case, you may consider implementing `Evaluate` for `T` such that
152/// `evaluate(x)` yields `f(x)`.
153/// This allows you to use `Thunk::new(x)` instead of `lazy!(f(x))`,
154/// saving time and space because
155/// any such `Thunk` will contain only `x` instead of both `f` and `x`.
156///
157/// Let us look at an example:
158///
159/// ~~~
160/// # use lazy_st::{Thunk, Evaluate};
161/// struct User(usize);
162///
163/// impl Evaluate<String> for User {
164///     fn evaluate(self) -> String {
165///         format!("User no. {}", self.0)
166///     }
167/// }
168///
169/// let root = Thunk::new(User(0));
170/// let mere_mortal = Thunk::evaluated(String::from("Someone else"));
171/// let user = if true { root } else { mere_mortal };
172/// assert_eq!(*user, "User no. 0");
173/// ~~~
174///
175/// Note that this trait is quite similar to the `Into` trait.
176/// Unfortunately, it seems that we cannot use `Into` here,
177/// because we cannot implement it for instances of `FnOnce`,
178/// which is necessary for `Lazy`.
179pub trait Evaluate<T> {
180    fn evaluate(self) -> T;
181}
182
183impl<A: FnOnce() -> B, B> Evaluate<B> for A {
184    fn evaluate(self) -> B {
185        self()
186    }
187}
188
189#[derive(Debug)]
190enum Inner<E, V> {
191    Unevaluated(E),
192    Evaluating,
193    Value(V),
194}
195
196impl<E, V> Clone for Thunk<E, V>
197where
198    E: Clone,
199    V: Clone,
200{
201    fn clone(&self) -> Self {
202        match unsafe { &*self.0.get() } {
203            Unevaluated(e) => Thunk(UnsafeCell::new(Unevaluated(e.clone()))),
204            Evaluating => panic!("Cloning thunk during evaluation."),
205            Value(v) => Thunk(UnsafeCell::new(Value(v.clone()))),
206        }
207    }
208}
209
210impl<E, V> Deref for Thunk<E, V>
211where
212    E: Evaluate<V>,
213{
214    type Target = V;
215
216    fn deref(&self) -> &V {
217        self.force();
218        match unsafe { &*self.0.get() } {
219            Value(ref v) => v,
220            _ => unreachable!(),
221        }
222    }
223}
224
225impl<E, V> DerefMut for Thunk<E, V>
226where
227    E: Evaluate<V>,
228{
229    fn deref_mut(&mut self) -> &mut V {
230        self.force();
231        match unsafe { &mut *self.0.get() } {
232            Value(ref mut v) => v,
233            _ => unreachable!(),
234        }
235    }
236}