Skip to main content

fp_library/classes/
send_deferrable.rs

1//! Deferred lazy evaluation using thread-safe thunks.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::*,
9//! 	types::*,
10//! };
11//!
12//! let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
13//! assert_eq!(*memo.evaluate(), 42);
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use fp_macros::*;
19	/// A trait for deferred lazy evaluation with thread-safe thunks.
20	///
21	/// `SendDeferrable` is the thread-safe counterpart of
22	/// [`Deferrable`](crate::classes::Deferrable). Where `Deferrable` accepts any
23	/// `FnOnce() -> Self + 'a`, this trait requires `FnOnce() -> Self + Send + 'a`,
24	/// ensuring the closure can be sent across thread boundaries. The two traits
25	/// are independent: implementing one does not require implementing the other.
26	///
27	/// Unlike [`SendCloneFn`](crate::classes::SendCloneFn), which wraps multi-use
28	/// `Fn` closures that are `Send + Sync`, this trait accepts a `FnOnce` closure that
29	/// only needs to be `Send` (not `Sync`), since deferred computations are executed
30	/// at most once.
31	///
32	/// ### Laws
33	///
34	/// `SendDeferrable` instances must satisfy the following law:
35	/// * Transparency: `send_defer(|| x)` is observationally equivalent to `x` when evaluated.
36	///
37	/// ### Why there is no generic `fix`
38	///
39	/// As with [`Deferrable`](crate::classes::Deferrable), lazy self-reference requires
40	/// shared ownership and interior mutability, which are properties specific to
41	/// [`Lazy`](crate::types::Lazy). The concrete function
42	/// [`arc_lazy_fix`](crate::types::lazy::arc_lazy_fix) provides this capability for
43	/// `ArcLazy` specifically.
44	#[document_type_parameters("The lifetime of the computation.")]
45	#[document_examples]
46	///
47	/// Transparency law for [`ArcLazy`](crate::types::ArcLazy):
48	///
49	/// ```
50	/// use fp_library::{
51	/// 	functions::*,
52	/// 	types::*,
53	/// };
54	///
55	/// // Transparency: send_defer(|| x) is equivalent to x when evaluated.
56	/// let x = ArcLazy::pure(42);
57	/// let deferred: ArcLazy<i32> = send_defer(|| ArcLazy::pure(42));
58	/// assert_eq!(*deferred.evaluate(), *x.evaluate());
59	/// ```
60	pub trait SendDeferrable<'a> {
61		/// Creates a deferred value from a thread-safe thunk.
62		#[document_signature]
63		///
64		#[document_parameters("The function that produces the value.")]
65		///
66		#[document_returns("A deferred value.")]
67		#[document_examples]
68		///
69		/// ```
70		/// use fp_library::{
71		/// 	brands::*,
72		/// 	functions::*,
73		/// 	types::*,
74		/// };
75		///
76		/// let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
77		/// assert_eq!(*memo.evaluate(), 42);
78		/// ```
79		fn send_defer(f: impl FnOnce() -> Self + Send + 'a) -> Self
80		where
81			Self: Sized;
82	}
83
84	/// Creates a deferred value from a thread-safe thunk.
85	///
86	/// Free function version that dispatches to [the type class' associated function][`SendDeferrable::send_defer`].
87	#[document_signature]
88	///
89	#[document_type_parameters(
90		"The lifetime of the computation.",
91		"The type of the deferred value."
92	)]
93	///
94	#[document_parameters("The function that produces the value.")]
95	///
96	#[document_returns("A deferred value.")]
97	#[document_examples]
98	///
99	/// ```
100	/// use fp_library::{
101	/// 	brands::*,
102	/// 	functions::*,
103	/// 	types::*,
104	/// };
105	///
106	/// let memo: ArcLazy<i32> = send_defer(|| ArcLazy::new(|| 42));
107	/// assert_eq!(*memo.evaluate(), 42);
108	/// ```
109	pub fn send_defer<'a, D: SendDeferrable<'a>>(f: impl FnOnce() -> D + Send + 'a) -> D {
110		D::send_defer(f)
111	}
112}
113
114pub use inner::*;
115
116#[cfg(test)]
117mod tests {
118	use {
119		crate::{
120			functions::*,
121			types::*,
122		},
123		quickcheck_macros::quickcheck,
124	};
125
126	/// SendDeferrable transparency law for `SendThunk`: `send_defer(|| x).evaluate() == x`.
127	#[quickcheck]
128	fn prop_send_deferrable_transparency_send_thunk(x: i32) -> bool {
129		let deferred: SendThunk<i32> = send_defer(|| SendThunk::pure(x));
130		deferred.evaluate() == x
131	}
132
133	/// SendDeferrable nesting law for `SendThunk`:
134	/// `send_defer(|| send_defer(|| x)).evaluate() == send_defer(|| x).evaluate()`.
135	#[quickcheck]
136	fn prop_send_deferrable_nesting_send_thunk(x: i32) -> bool {
137		let nested: SendThunk<i32> = send_defer(|| send_defer(|| SendThunk::pure(x)));
138		let single: SendThunk<i32> = send_defer(|| SendThunk::pure(x));
139		nested.evaluate() == single.evaluate()
140	}
141
142	/// SendDeferrable transparency law for `ArcLazy`: `send_defer(|| x).evaluate() == x`.
143	#[quickcheck]
144	fn prop_send_deferrable_transparency_arc_lazy(x: i32) -> bool {
145		let deferred: ArcLazy<i32> = send_defer(|| ArcLazy::pure(x));
146		*deferred.evaluate() == x
147	}
148
149	/// SendDeferrable nesting law for `ArcLazy`:
150	/// `send_defer(|| send_defer(|| x)).evaluate() == send_defer(|| x).evaluate()`.
151	#[quickcheck]
152	fn prop_send_deferrable_nesting_arc_lazy(x: i32) -> bool {
153		let nested: ArcLazy<i32> = send_defer(|| send_defer(|| ArcLazy::pure(x)));
154		let single: ArcLazy<i32> = send_defer(|| ArcLazy::pure(x));
155		*nested.evaluate() == *single.evaluate()
156	}
157
158	/// SendDeferrable transparency law for `TrySendThunk`:
159	/// `send_defer(|| x).evaluate() == Ok(x)`.
160	#[quickcheck]
161	fn prop_send_deferrable_transparency_try_send_thunk(x: i32) -> bool {
162		let deferred: TrySendThunk<i32, String> = send_defer(|| TrySendThunk::pure(x));
163		deferred.evaluate() == Ok(x)
164	}
165
166	/// SendDeferrable nesting law for `TrySendThunk`:
167	/// `send_defer(|| send_defer(|| x)).evaluate() == send_defer(|| x).evaluate()`.
168	#[quickcheck]
169	fn prop_send_deferrable_nesting_try_send_thunk(x: i32) -> bool {
170		let nested: TrySendThunk<i32, String> = send_defer(|| send_defer(|| TrySendThunk::pure(x)));
171		let single: TrySendThunk<i32, String> = send_defer(|| TrySendThunk::pure(x));
172		nested.evaluate() == single.evaluate()
173	}
174
175	/// SendDeferrable transparency law for `ArcTryLazy`:
176	/// `send_defer(|| x).evaluate() == Ok(&x)`.
177	#[quickcheck]
178	fn prop_send_deferrable_transparency_arc_try_lazy(x: i32) -> bool {
179		let deferred: ArcTryLazy<i32, String> = send_defer(|| ArcTryLazy::ok(x));
180		deferred.evaluate() == Ok(&x)
181	}
182
183	/// SendDeferrable nesting law for `ArcTryLazy`:
184	/// `send_defer(|| send_defer(|| x)).evaluate() == send_defer(|| x).evaluate()`.
185	#[quickcheck]
186	fn prop_send_deferrable_nesting_arc_try_lazy(x: i32) -> bool {
187		let nested: ArcTryLazy<i32, String> = send_defer(|| send_defer(|| ArcTryLazy::ok(x)));
188		let single: ArcTryLazy<i32, String> = send_defer(|| ArcTryLazy::ok(x));
189		nested.evaluate() == single.evaluate()
190	}
191}