Skip to main content

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::*;