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}