fp_library/classes/ref_monad.rs
1//! By-ref monads, combining [`RefApplicative`](crate::classes::RefApplicative) and [`RefSemimonad`](crate::classes::RefSemimonad).
2//!
3//! This is the by-ref counterpart of [`Monad`](crate::classes::Monad).
4//! Enables monadic sequencing where the continuation receives `&A` instead
5//! of owned `A`, and value injection clones from `&A`.
6//!
7//! ### Examples
8//!
9//! ```
10//! use fp_library::{
11//! brands::*,
12//! classes::*,
13//! functions::{
14//! explicit::bind,
15//! *,
16//! },
17//! types::*,
18//! };
19//!
20//! // Chain computations on memoized values by reference
21//! let lazy = ref_pure::<LazyBrand<RcLazyConfig>, _>(&5);
22//! let result = bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&lazy, |x: &i32| {
23//! let v = *x * 2;
24//! ref_pure::<LazyBrand<RcLazyConfig>, _>(&v)
25//! });
26//! assert_eq!(*result.evaluate(), 10);
27//! ```
28
29#[fp_macros::document_module]
30mod inner {
31 use {
32 crate::{
33 classes::*,
34 kinds::*,
35 },
36 fp_macros::*,
37 };
38
39 /// A type class for by-ref monads.
40 ///
41 /// Combines [`RefApplicative`] (by-ref pure + apply) with
42 /// [`RefSemimonad`] (by-ref bind).
43 ///
44 /// This is the by-ref counterpart of [`Monad`]. Automatically
45 /// implemented for any type implementing both supertraits.
46 ///
47 /// A lawful `RefMonad` must satisfy three laws:
48 ///
49 /// 1. **Left identity**: `bind(ref_pure(&a), f)` evaluates to the
50 /// same value as `f(&a)`.
51 /// 2. **Right identity**: `bind(m, |x| ref_pure(x))` evaluates to
52 /// the same value as `m`.
53 /// 3. **Associativity**: `bind(bind(m, f), g)` evaluates to the
54 /// same value as `bind(m, |x| bind(f(x), g))`.
55 ///
56 /// These are the standard monad laws expressed with by-ref operations.
57 /// Equality is by evaluated value, not structural identity, since
58 /// memoized types like [`Lazy`](crate::types::Lazy) create new
59 /// allocations on each construction.
60 #[document_examples]
61 ///
62 /// ```
63 /// use fp_library::{
64 /// brands::*,
65 /// classes::*,
66 /// functions::{
67 /// explicit::bind,
68 /// *,
69 /// },
70 /// types::*,
71 /// };
72 ///
73 /// let f = |x: &i32| {
74 /// let v = *x + 1;
75 /// Lazy::<_, RcLazyConfig>::new(move || v)
76 /// };
77 /// let g = |x: &i32| {
78 /// let v = *x * 2;
79 /// Lazy::<_, RcLazyConfig>::new(move || v)
80 /// };
81 ///
82 /// // Left identity: bind(ref_pure(&a), f) = f(&a)
83 /// let left =
84 /// bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&ref_pure::<LazyBrand<RcLazyConfig>, _>(&5), f);
85 /// assert_eq!(*left.evaluate(), *f(&5).evaluate());
86 ///
87 /// // Right identity: bind(m, |x| ref_pure(x)) = m
88 /// let m = RcLazy::pure(42);
89 /// let right = bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&m, |x: &i32| {
90 /// ref_pure::<LazyBrand<RcLazyConfig>, _>(x)
91 /// });
92 /// assert_eq!(*right.evaluate(), *m.evaluate());
93 ///
94 /// // Associativity: bind(bind(m, f), g) = bind(m, |x| bind(f(x), g))
95 /// let m = RcLazy::pure(3);
96 /// let lhs = bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(
97 /// &bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&m, f),
98 /// g,
99 /// );
100 /// let rhs = bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&m, |x: &i32| {
101 /// bind::<LazyBrand<RcLazyConfig>, _, _, _, _>(&f(x), g)
102 /// });
103 /// assert_eq!(*lhs.evaluate(), *rhs.evaluate());
104 /// ```
105 pub trait RefMonad: RefApplicative + RefSemimonad {}
106
107 /// Blanket implementation of [`RefMonad`].
108 #[document_type_parameters("The brand type.")]
109 impl<Brand> RefMonad for Brand where Brand: RefApplicative + RefSemimonad {}
110
111 /// Executes a monadic action conditionally, using by-ref bind.
112 ///
113 /// Evaluates the monadic boolean condition by reference, then returns
114 /// one of the two branches depending on the result.
115 #[document_signature]
116 ///
117 #[document_type_parameters(
118 "The lifetime of the computations.",
119 "The brand of the monad.",
120 "The type of the result."
121 )]
122 ///
123 #[document_parameters(
124 "The monadic boolean condition.",
125 "The value if true.",
126 "The value if false."
127 )]
128 ///
129 #[document_returns("The selected branch.")]
130 #[document_examples]
131 ///
132 /// ```
133 /// use fp_library::{
134 /// brands::*,
135 /// functions::*,
136 /// types::*,
137 /// };
138 ///
139 /// let cond = RcLazy::pure(true);
140 /// let then_val = RcLazy::pure(1);
141 /// let else_val = RcLazy::pure(0);
142 /// let result = ref_if_m::<LazyBrand<RcLazyConfig>, _>(&cond, &then_val, &else_val);
143 /// assert_eq!(*result.evaluate(), 1);
144 /// ```
145 pub fn ref_if_m<'a, Brand: RefMonad, A: 'a>(
146 cond: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, bool>),
147 then_branch: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
148 else_branch: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
149 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
150 where
151 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
152 let then_branch = then_branch.clone();
153 let else_branch = else_branch.clone();
154 Brand::ref_bind(
155 cond,
156 move |c: &bool| {
157 if *c { then_branch.clone() } else { else_branch.clone() }
158 },
159 )
160 }
161
162 /// Performs a monadic action when a by-ref condition is false.
163 ///
164 /// Evaluates the monadic boolean condition by reference, then executes
165 /// the action if the result is `false`, otherwise returns `ref_pure(&())`.
166 #[document_signature]
167 ///
168 #[document_type_parameters("The lifetime of the computations.", "The brand of the monad.")]
169 ///
170 #[document_parameters("The monadic boolean condition.", "The action to execute if false.")]
171 ///
172 #[document_returns("The action result, or a pure unit value.")]
173 #[document_examples]
174 ///
175 /// ```
176 /// use fp_library::{
177 /// brands::*,
178 /// functions::*,
179 /// types::*,
180 /// };
181 ///
182 /// let cond = RcLazy::pure(false);
183 /// let action = RcLazy::pure(());
184 /// let result = ref_unless_m::<LazyBrand<RcLazyConfig>>(&cond, &action);
185 /// assert_eq!(*result.evaluate(), ());
186 /// ```
187 pub fn ref_unless_m<'a, Brand: RefMonad>(
188 cond: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, bool>),
189 action: &Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>),
190 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>)
191 where
192 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>): Clone, {
193 let action = action.clone();
194 Brand::ref_bind(
195 cond,
196 move |c: &bool| {
197 if *c { Brand::ref_pure(&()) } else { action.clone() }
198 },
199 )
200 }
201}
202
203pub use inner::*;