Skip to main content

fp_library/classes/
deferrable.rs

1//! Types that can be constructed lazily from a computation.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	functions::*,
8//! 	types::*,
9//! };
10//!
11//! let eval: Thunk<i32> = defer(|| Thunk::pure(42));
12//! assert_eq!(eval.evaluate(), 42);
13//! ```
14
15#[fp_macros::document_module]
16mod inner {
17	use fp_macros::*;
18	/// A type class for types that can be constructed lazily.
19	///
20	/// `Deferrable` is the inverse of [`Extract`](crate::classes::Extract): where
21	/// `Extract` forces/extracts the inner value, `Deferrable` constructs a value
22	/// lazily from a thunk. For types whose brand implements `Extract` (e.g.,
23	/// [`ThunkBrand`](crate::brands::ThunkBrand)), `extract(defer(|| x)) == x`
24	/// forms a round-trip. Note that `Deferrable` is a value-level trait
25	/// (implemented by concrete types like `Thunk`), while `Extract` is a
26	/// brand-level trait (implemented by `ThunkBrand`).
27	///
28	/// ### Laws
29	///
30	/// `Deferrable` instances must satisfy the following law:
31	/// * Transparency: The value produced by `defer(|| x)` is identical to `x`. This law
32	///   does not constrain *when* evaluation occurs; some implementations may evaluate eagerly.
33	///
34	/// ### Why there is no generic `fix`
35	///
36	/// In PureScript, `fix :: Lazy l => (l -> l) -> l` enables lazy self-reference,
37	/// which is essential for tying the knot in recursive values. In Rust, lazy
38	/// self-reference requires shared ownership (`Rc`/`Arc`) and interior mutability,
39	/// which are properties specific to [`Lazy`](crate::types::Lazy) rather than
40	/// all `Deferrable` types. For example, [`Thunk`](crate::types::Thunk) is consumed
41	/// on evaluation, so self-referential construction is not possible.
42	///
43	/// The concrete functions [`rc_lazy_fix`](crate::types::lazy::rc_lazy_fix) and
44	/// [`arc_lazy_fix`](crate::types::lazy::arc_lazy_fix) provide this capability for
45	/// `Lazy` specifically.
46	///
47	/// `Deferrable` is for single-threaded deferred construction. For thread-safe
48	/// deferred construction with `Send` closures, use
49	/// [`SendDeferrable`](crate::classes::SendDeferrable).
50	#[document_type_parameters("The lifetime of the computation.")]
51	#[document_examples]
52	///
53	/// Transparency law for [`Thunk`](crate::types::Thunk):
54	///
55	/// ```
56	/// use fp_library::{
57	/// 	functions::*,
58	/// 	types::*,
59	/// };
60	///
61	/// // Transparency: defer(|| x) is equivalent to x when evaluated.
62	/// let x = Thunk::pure(42);
63	/// let deferred: Thunk<i32> = defer(|| Thunk::pure(42));
64	/// assert_eq!(deferred.evaluate(), x.evaluate());
65	/// ```
66	pub trait Deferrable<'a> {
67		/// Creates a value from a computation that produces the value.
68		///
69		/// This function takes a thunk and creates a deferred value that will be computed using the thunk.
70		#[document_signature]
71		///
72		#[document_parameters("A thunk that produces the value.")]
73		///
74		#[document_returns("The deferred value.")]
75		#[document_examples]
76		///
77		/// ```
78		/// use fp_library::{
79		/// 	functions::*,
80		/// 	types::*,
81		/// };
82		///
83		/// let eval: Thunk<i32> = defer(|| Thunk::pure(42));
84		/// assert_eq!(eval.evaluate(), 42);
85		/// ```
86		fn defer(f: impl FnOnce() -> Self + 'a) -> Self
87		where
88			Self: Sized;
89	}
90
91	/// Creates a value from a computation that produces the value.
92	///
93	/// Free function version that dispatches to [the type class' associated function][`Deferrable::defer`].
94	#[document_signature]
95	///
96	#[document_type_parameters(
97		"The lifetime of the computation.",
98		"The type of the deferred value."
99	)]
100	///
101	#[document_parameters("A thunk that produces the value.")]
102	///
103	#[document_returns("The deferred value.")]
104	#[document_examples]
105	///
106	/// ```
107	/// use fp_library::{
108	/// 	functions::*,
109	/// 	types::*,
110	/// };
111	///
112	/// let eval: Thunk<i32> = defer(|| Thunk::pure(42));
113	/// assert_eq!(eval.evaluate(), 42);
114	/// ```
115	pub fn defer<'a, D: Deferrable<'a>>(f: impl FnOnce() -> D + 'a) -> D {
116		D::defer(f)
117	}
118}
119
120pub use inner::*;
121
122#[cfg(test)]
123mod tests {
124	use {
125		crate::{
126			functions::*,
127			types::*,
128		},
129		quickcheck_macros::quickcheck,
130	};
131
132	/// Deferrable transparency law: evaluate(defer(|| x)) == x.
133	#[quickcheck]
134	fn prop_deferrable_transparency(x: i32) -> bool {
135		let deferred: Thunk<i32> = defer(|| Thunk::pure(x));
136		deferred.evaluate() == x
137	}
138
139	/// Deferrable nesting law: evaluate(defer(|| defer(|| x))) == evaluate(defer(|| x)).
140	#[quickcheck]
141	fn prop_deferrable_nesting(x: i32) -> bool {
142		let nested: Thunk<i32> = defer(|| defer(|| Thunk::pure(x)));
143		let single: Thunk<i32> = defer(|| Thunk::pure(x));
144		nested.evaluate() == single.evaluate()
145	}
146}