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}