fp_library/classes/monad.rs
1//! Monads, allowing for sequencing computations where the structure depends on previous results.
2//!
3//! A monad combines [`Pointed`][crate::classes::Pointed] (for lifting values with
4//! [`pure`][crate::functions::pure]) and [`Semimonad`][crate::classes::Semimonad]
5//! (for chaining computations with [`bind`][crate::functions::bind]).
6//! The [`m_do!`][fp_macros::m_do] macro provides do-notation for writing monadic code
7//! in a flat, readable style.
8//!
9//! ### Examples
10//!
11//! Chaining fallible computations with [`Option`]:
12//!
13//! ```
14//! use fp_library::{brands::*, functions::*};
15//! use fp_macros::m_do;
16//!
17//! fn safe_div(a: i32, b: i32) -> Option<i32> {
18//! if b == 0 { None } else { Some(a / b) }
19//! }
20//!
21//! // Each `<-` extracts the value; None short-circuits the whole block
22//! let result = m_do!(OptionBrand {
23//! x <- safe_div(100, 2);
24//! y <- safe_div(x, 5);
25//! pure(y + 1)
26//! });
27//! assert_eq!(result, Some(11));
28//!
29//! // Short-circuits on failure
30//! let result = m_do!(OptionBrand {
31//! x <- safe_div(100, 0);
32//! y <- safe_div(x, 5);
33//! pure(y + 1)
34//! });
35//! assert_eq!(result, None);
36//! ```
37//!
38//! List comprehensions with [`Vec`]:
39//!
40//! ```
41//! use fp_library::{brands::*, functions::*};
42//! use fp_macros::m_do;
43//!
44//! // Generate Pythagorean triples up to 10
45//! let triples = m_do!(VecBrand {
46//! x <- (1..=10i32).collect::<Vec<_>>();
47//! y <- (x..=10).collect::<Vec<_>>();
48//! z <- (y..=10).collect::<Vec<_>>();
49//! _ <- if x * x + y * y == z * z { vec![()] } else { vec![] };
50//! pure((x, y, z))
51//! });
52//! assert_eq!(triples, vec![(3, 4, 5), (6, 8, 10)]);
53//! ```
54//!
55//! Error handling with [`Result`]:
56//!
57//! ```
58//! use fp_library::{brands::*, functions::*};
59//! use fp_macros::m_do;
60//!
61//! fn parse_field(input: &str) -> Result<i32, String> {
62//! input.parse().map_err(|_| format!("invalid: {}", input))
63//! }
64//!
65//! let result: Result<i32, String> = m_do!(ResultErrAppliedBrand<String> {
66//! x <- parse_field("10");
67//! y <- parse_field("20");
68//! let sum = x + y;
69//! pure(sum)
70//! });
71//! assert_eq!(result, Ok(30));
72//!
73//! let result: Result<i32, String> = m_do!(ResultErrAppliedBrand<String> {
74//! x <- parse_field("10");
75//! y <- parse_field("abc");
76//! pure(x + y)
77//! });
78//! assert_eq!(result, Err("invalid: abc".to_string()));
79//! ```
80//!
81//! The `m_do!` macro supports typed bindings, let bindings, sequencing, and
82//! automatic `pure` rewriting:
83//!
84//! ```
85//! use fp_library::{brands::*, functions::*};
86//! use fp_macros::m_do;
87//!
88//! // Typed bindings
89//! let r = m_do!(OptionBrand { x: i32 <- Some(5); pure(x * 2) });
90//! assert_eq!(r, Some(10));
91//!
92//! // Let bindings for pure local computations
93//! let r = m_do!(OptionBrand {
94//! x <- Some(5);
95//! let y = x * 2;
96//! pure(y)
97//! });
98//! assert_eq!(r, Some(10));
99//!
100//! // Sequencing: execute for effects, discard result
101//! let r = m_do!(OptionBrand { Some(()); pure(42) });
102//! assert_eq!(r, Some(42));
103//!
104//! // `pure(...)` is auto-rewritten with the correct brand
105//! let r = m_do!(OptionBrand {
106//! x <- Some(5);
107//! y <- pure(x + 1);
108//! pure(x + y)
109//! });
110//! assert_eq!(r, Some(11));
111//! ```
112
113#[fp_macros::document_module]
114mod inner {
115 use {
116 crate::{
117 classes::*,
118 kinds::*,
119 },
120 fp_macros::*,
121 };
122
123 /// A type class for monads, allowing for sequencing computations where the
124 /// structure of the computation depends on the result of the previous
125 /// computation.
126 ///
127 /// `class (Applicative m, Semimonad m) => Monad m`
128 ///
129 /// A lawful `Monad` must satisfy three laws:
130 ///
131 /// 1. **Left identity**: `bind(pure(a), f) ≡ f(a)` — lifting a value and
132 /// immediately binding it is the same as applying the function directly.
133 /// 2. **Right identity**: `bind(m, pure) ≡ m` — binding a computation to
134 /// `pure` leaves it unchanged.
135 /// 3. **Associativity**: `bind(bind(m, f), g) ≡ bind(m, |x| bind(f(x), g))`
136 /// — the order of nesting doesn't matter, only the order of operations.
137 #[document_examples]
138 ///
139 /// Monad laws for [`Option`]:
140 ///
141 /// ```
142 /// use fp_library::{brands::*, functions::*};
143 /// use fp_macros::m_do;
144 ///
145 /// let f = |x: i32| Some(x + 1);
146 /// let g = |x: i32| Some(x * 2);
147 ///
148 /// // Left identity: bind(pure(a), f) ≡ f(a)
149 /// assert_eq!(
150 /// bind::<OptionBrand, _, _>(pure::<OptionBrand, _>(5), f),
151 /// f(5),
152 /// );
153 /// // With m_do!: wrapping in pure then binding is the same as calling f
154 /// assert_eq!(
155 /// m_do!(OptionBrand { x <- pure(5); pure(x + 1) }),
156 /// Some(6),
157 /// );
158 ///
159 /// // Right identity: bind(m, pure) ≡ m
160 /// assert_eq!(
161 /// bind::<OptionBrand, _, _>(Some(42), pure::<OptionBrand, _>),
162 /// Some(42),
163 /// );
164 /// // With m_do!: extracting and re-wrapping is a no-op
165 /// assert_eq!(
166 /// m_do!(OptionBrand { x <- Some(42); pure(x) }),
167 /// Some(42),
168 /// );
169 ///
170 /// // Associativity: bind(bind(m, f), g) ≡ bind(m, |x| bind(f(x), g))
171 /// assert_eq!(
172 /// bind::<OptionBrand, _, _>(
173 /// bind::<OptionBrand, _, _>(Some(5), f),
174 /// g,
175 /// ),
176 /// bind::<OptionBrand, _, _>(Some(5), |x| bind::<OptionBrand, _, _>(f(x), g)),
177 /// );
178 /// // With m_do!: sequential binds compose naturally
179 /// assert_eq!(
180 /// m_do!(OptionBrand { x <- Some(5); y <- pure(x + 1); pure(y * 2) }),
181 /// Some(12),
182 /// );
183 /// ```
184 ///
185 /// Monad laws for [`Vec`]:
186 ///
187 /// ```
188 /// use fp_library::{
189 /// brands::*,
190 /// functions::*,
191 /// };
192 ///
193 /// let f = |x: i32| vec![x, x + 1];
194 /// let g = |x: i32| vec![x * 10];
195 ///
196 /// // Left identity: bind(pure(a), f) ≡ f(a)
197 /// assert_eq!(bind::<VecBrand, _, _>(pure::<VecBrand, _>(3), f), f(3),);
198 ///
199 /// // Right identity: bind(m, pure) ≡ m
200 /// assert_eq!(bind::<VecBrand, _, _>(vec![1, 2, 3], pure::<VecBrand, _>), vec![1, 2, 3],);
201 ///
202 /// // Associativity: bind(bind(m, f), g) ≡ bind(m, |x| bind(f(x), g))
203 /// let m = vec![1, 2];
204 /// assert_eq!(
205 /// bind::<VecBrand, _, _>(bind::<VecBrand, _, _>(m.clone(), f), g,),
206 /// bind::<VecBrand, _, _>(m, |x| bind::<VecBrand, _, _>(f(x), g)),
207 /// );
208 /// ```
209 pub trait Monad: Applicative + Semimonad {}
210
211 /// Blanket implementation of [`Monad`].
212 #[document_type_parameters("The brand type.")]
213 impl<Brand> Monad for Brand where Brand: Applicative + Semimonad {}
214
215 /// Executes a monadic action conditionally.
216 ///
217 /// Evaluates the monadic boolean condition, then returns one of the two branches
218 /// depending on the result. Both branches are provided as monadic values.
219 #[document_signature]
220 ///
221 #[document_type_parameters(
222 "The lifetime of the computations.",
223 "The brand of the monad.",
224 "The type of the value produced by each branch."
225 )]
226 ///
227 #[document_parameters(
228 "A monadic computation that produces a boolean.",
229 "The computation to execute if the condition is `true`.",
230 "The computation to execute if the condition is `false`."
231 )]
232 ///
233 #[document_returns("The result of the selected branch.")]
234 #[document_examples]
235 ///
236 /// ```
237 /// use fp_library::{
238 /// brands::*,
239 /// functions::*,
240 /// };
241 ///
242 /// let result = if_m::<OptionBrand, _>(Some(true), Some(1), Some(0));
243 /// assert_eq!(result, Some(1));
244 ///
245 /// let result = if_m::<OptionBrand, _>(Some(false), Some(1), Some(0));
246 /// assert_eq!(result, Some(0));
247 ///
248 /// let result = if_m::<OptionBrand, i32>(None, Some(1), Some(0));
249 /// assert_eq!(result, None);
250 /// ```
251 pub fn if_m<'a, Brand: Monad, A: 'a>(
252 cond: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, bool>),
253 then_branch: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
254 else_branch: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
255 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)
256 where
257 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
258 Brand::bind(cond, move |c| if c { then_branch.clone() } else { else_branch.clone() })
259 }
260
261 /// Performs a monadic action when a monadic condition is true.
262 ///
263 /// Evaluates the monadic boolean condition, then executes the action if the
264 /// result is `true`, otherwise returns `pure(())`.
265 #[document_signature]
266 ///
267 #[document_type_parameters("The lifetime of the computations.", "The brand of the monad.")]
268 ///
269 #[document_parameters(
270 "A monadic computation that produces a boolean.",
271 "The action to perform if the condition is true."
272 )]
273 ///
274 #[document_returns("The action if the condition is true, otherwise `pure(())`.")]
275 #[document_examples]
276 ///
277 /// ```
278 /// use fp_library::{
279 /// brands::*,
280 /// functions::*,
281 /// };
282 ///
283 /// assert_eq!(when_m::<OptionBrand>(Some(true), Some(())), Some(()));
284 /// assert_eq!(when_m::<OptionBrand>(Some(false), Some(())), Some(()));
285 /// assert_eq!(when_m::<OptionBrand>(None, Some(())), None);
286 /// ```
287 pub fn when_m<'a, Brand: Monad>(
288 cond: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, bool>),
289 action: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>),
290 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>)
291 where
292 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>): Clone, {
293 Brand::bind(cond, move |c| if c { action.clone() } else { Brand::pure(()) })
294 }
295
296 /// Performs a monadic action unless a monadic condition is true.
297 ///
298 /// Evaluates the monadic boolean condition, then executes the action if the
299 /// result is `false`, otherwise returns `pure(())`.
300 #[document_signature]
301 ///
302 #[document_type_parameters("The lifetime of the computations.", "The brand of the monad.")]
303 ///
304 #[document_parameters(
305 "A monadic computation that produces a boolean.",
306 "The action to perform if the condition is false."
307 )]
308 ///
309 #[document_returns("The action if the condition is false, otherwise `pure(())`.")]
310 #[document_examples]
311 ///
312 /// ```
313 /// use fp_library::{
314 /// brands::*,
315 /// functions::*,
316 /// };
317 ///
318 /// assert_eq!(unless_m::<OptionBrand>(Some(false), Some(())), Some(()));
319 /// assert_eq!(unless_m::<OptionBrand>(Some(true), Some(())), Some(()));
320 /// assert_eq!(unless_m::<OptionBrand>(None, Some(())), None);
321 /// ```
322 pub fn unless_m<'a, Brand: Monad>(
323 cond: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, bool>),
324 action: Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>),
325 ) -> Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>)
326 where
327 Apply!(<Brand as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ()>): Clone, {
328 Brand::bind(cond, move |c| if !c { action.clone() } else { Brand::pure(()) })
329 }
330}
331
332pub use inner::*;