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