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}