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}