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