Skip to main content

fp_library/classes/
ref_bifunctor.rs

1//! Types that can be mapped over two type arguments simultaneously by reference.
2//!
3//! ### Examples
4//!
5//! ```
6//! use fp_library::{
7//! 	brands::*,
8//! 	functions::explicit::*,
9//! };
10//!
11//! let x = Result::<i32, i32>::Ok(5);
12//! let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
13//! assert_eq!(y, Ok(10));
14//! ```
15
16#[fp_macros::document_module]
17mod inner {
18	use {
19		crate::{
20			brands::*,
21			classes::*,
22			kinds::*,
23		},
24		fp_macros::*,
25	};
26
27	/// A type class for types that can be mapped over two type arguments by reference.
28	///
29	/// This is the by-reference variant of [`Bifunctor`]. Both closures receive references
30	/// to the values (`&A` and `&C`) and produce owned output (`B` and `D`). The container
31	/// is borrowed, not consumed.
32	///
33	/// Unlike [`RefFunctor`] for partially-applied bifunctor brands (e.g.,
34	/// `ResultErrAppliedBrand<E>`), `RefBifunctor` does not require `Clone` on either
35	/// type parameter because both sides have closures to handle their respective types.
36	///
37	/// ### Laws
38	///
39	/// `RefBifunctor` instances must satisfy the following laws:
40	///
41	/// **Identity:** `ref_bimap(|x| x.clone(), |x| x.clone(), &p)` is equivalent to
42	/// `p.clone()`, given `A: Clone, C: Clone`.
43	///
44	/// **Composition:** `ref_bimap(|x| f2(&f1(x)), |x| g2(&g1(x)), &p)` is equivalent to
45	/// `ref_bimap(f2, g2, &ref_bimap(f1, g1, &p))`.
46	#[document_examples]
47	///
48	/// RefBifunctor laws for [`Result`]:
49	///
50	/// ```
51	/// use fp_library::{
52	/// 	brands::*,
53	/// 	functions::{
54	/// 		explicit::bimap,
55	/// 		*,
56	/// 	},
57	/// };
58	///
59	/// let ok: Result<i32, i32> = Ok(5);
60	/// let err: Result<i32, i32> = Err(3);
61	///
62	/// // Identity: ref_bimap(Clone::clone, Clone::clone, &p) == p.clone()
63	/// assert_eq!(bimap::<ResultBrand, _, _, _, _, _, _>((|x: &i32| *x, |x: &i32| *x), &ok), ok,);
64	/// assert_eq!(bimap::<ResultBrand, _, _, _, _, _, _>((|x: &i32| *x, |x: &i32| *x), &err), err,);
65	///
66	/// // Composition: bimap((compose(f1, f2), compose(g1, g2)), &p)
67	/// //            = bimap((f2, g2), &bimap((f1, g1), &p))
68	/// let f1 = |x: &i32| *x + 1;
69	/// let f2 = |x: &i32| *x * 2;
70	/// let g1 = |x: &i32| *x + 10;
71	/// let g2 = |x: &i32| *x * 3;
72	/// assert_eq!(
73	/// 	bimap::<ResultBrand, _, _, _, _, _, _>((|x: &i32| f2(&f1(x)), |x: &i32| g2(&g1(x))), &ok),
74	/// 	bimap::<ResultBrand, _, _, _, _, _, _>(
75	/// 		(f2, g2),
76	/// 		&bimap::<ResultBrand, _, _, _, _, _, _>((f1, g1), &ok),
77	/// 	),
78	/// );
79	/// ```
80	#[kind(type Of<'a, A: 'a, B: 'a>: 'a;)]
81	pub trait RefBifunctor {
82		/// Maps functions over the values in the bifunctor context by reference.
83		///
84		/// Both closures receive references to the values and produce owned output.
85		/// The container is borrowed, not consumed.
86		#[document_signature]
87		///
88		#[document_type_parameters(
89			"The lifetime of the values.",
90			"The type of the first value.",
91			"The type of the first result.",
92			"The type of the second value.",
93			"The type of the second result."
94		)]
95		///
96		#[document_parameters(
97			"The function to apply to the first value.",
98			"The function to apply to the second value.",
99			"The bifunctor instance (borrowed)."
100		)]
101		///
102		#[document_returns(
103			"A new bifunctor instance containing the results of applying the functions."
104		)]
105		#[document_examples]
106		///
107		/// ```
108		/// use fp_library::{
109		/// 	brands::*,
110		/// 	classes::*,
111		/// };
112		///
113		/// let x: Result<i32, i32> = Ok(5);
114		/// let y = ResultBrand::ref_bimap(|e: &i32| *e + 1, |s: &i32| *s * 2, &x);
115		/// assert_eq!(y, Ok(10));
116		/// ```
117		fn ref_bimap<'a, A: 'a, B: 'a, C: 'a, D: 'a>(
118			f: impl Fn(&A) -> B + 'a,
119			g: impl Fn(&C) -> D + 'a,
120			p: &Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
121		) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>);
122
123		/// Maps a function over the first type argument of the bifunctor by reference.
124		///
125		/// By-reference variant of [`Bifunctor::map_first`]. The closure receives a reference
126		/// to the first value and produces an owned result. Requires `C: Clone` because the
127		/// second value must be cloned out of the borrowed container.
128		#[document_signature]
129		///
130		#[document_type_parameters(
131			"The lifetime of the values.",
132			"The type of the first value.",
133			"The type of the first result.",
134			"The type of the second value (must be Clone)."
135		)]
136		///
137		#[document_parameters(
138			"The function to apply to the first value.",
139			"The bifunctor instance (borrowed)."
140		)]
141		///
142		#[document_returns("A new bifunctor instance with the first value transformed.")]
143		#[document_examples]
144		///
145		/// ```
146		/// use fp_library::{
147		/// 	brands::*,
148		/// 	classes::*,
149		/// };
150		///
151		/// let x = Result::<i32, i32>::Err(5);
152		/// let y = ResultBrand::ref_map_first(|e: &i32| *e * 2, &x);
153		/// assert_eq!(y, Err(10));
154		///
155		/// let x = Result::<i32, i32>::Ok(5);
156		/// let y = ResultBrand::ref_map_first(|e: &i32| *e * 2, &x);
157		/// assert_eq!(y, Ok(5));
158		/// ```
159		fn ref_map_first<'a, A: 'a, B: 'a, C: Clone + 'a>(
160			f: impl Fn(&A) -> B + 'a,
161			p: &Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
162		) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, C>) {
163			Self::ref_bimap(f, |c: &C| c.clone(), p)
164		}
165
166		/// Maps a function over the second type argument of the bifunctor by reference.
167		///
168		/// By-reference variant of [`Bifunctor::map_second`]. The closure receives a reference
169		/// to the second value and produces an owned result. Requires `A: Clone` because the
170		/// first value must be cloned out of the borrowed container.
171		#[document_signature]
172		///
173		#[document_type_parameters(
174			"The lifetime of the values.",
175			"The type of the first value (must be Clone).",
176			"The type of the second value.",
177			"The type of the second result."
178		)]
179		///
180		#[document_parameters(
181			"The function to apply to the second value.",
182			"The bifunctor instance (borrowed)."
183		)]
184		///
185		#[document_returns("A new bifunctor instance with the second value transformed.")]
186		#[document_examples]
187		///
188		/// ```
189		/// use fp_library::{
190		/// 	brands::*,
191		/// 	classes::*,
192		/// };
193		///
194		/// let x = Result::<i32, i32>::Ok(5);
195		/// let y = ResultBrand::ref_map_second(|s: &i32| *s * 2, &x);
196		/// assert_eq!(y, Ok(10));
197		///
198		/// let x = Result::<i32, i32>::Err(5);
199		/// let y = ResultBrand::ref_map_second(|s: &i32| *s * 2, &x);
200		/// assert_eq!(y, Err(5));
201		/// ```
202		fn ref_map_second<'a, A: Clone + 'a, B: 'a, C: 'a>(
203			g: impl Fn(&B) -> C + 'a,
204			p: &Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
205		) -> Apply!(<Self as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>) {
206			Self::ref_bimap(|a: &A| a.clone(), g, p)
207		}
208	}
209
210	/// Maps functions over the values in the bifunctor context by reference.
211	///
212	/// Free function version that dispatches to [the type class' associated function][`RefBifunctor::ref_bimap`].
213	#[document_signature]
214	///
215	#[document_type_parameters(
216		"The lifetime of the values.",
217		"The brand of the bifunctor.",
218		"The type of the first value.",
219		"The type of the first result.",
220		"The type of the second value.",
221		"The type of the second result."
222	)]
223	///
224	#[document_parameters(
225		"The function to apply to the first value.",
226		"The function to apply to the second value.",
227		"The bifunctor instance (borrowed)."
228	)]
229	///
230	#[document_returns(
231		"A new bifunctor instance containing the results of applying the functions."
232	)]
233	#[document_examples]
234	///
235	/// ```
236	/// use fp_library::{
237	/// 	brands::*,
238	/// 	functions::explicit::*,
239	/// };
240	///
241	/// let x = Result::<i32, i32>::Ok(5);
242	/// let y = bimap::<ResultBrand, _, _, _, _, _, _>((|e: &i32| *e + 1, |s: &i32| *s * 2), &x);
243	/// assert_eq!(y, Ok(10));
244	/// ```
245	pub fn ref_bimap<'a, Brand: RefBifunctor, A: 'a, B: 'a, C: 'a, D: 'a>(
246		f: impl Fn(&A) -> B + 'a,
247		g: impl Fn(&C) -> D + 'a,
248		p: &Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
249	) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, D>) {
250		Brand::ref_bimap(f, g, p)
251	}
252
253	/// Maps a function over the first type argument of the bifunctor by reference.
254	///
255	/// Free function version that dispatches to [the type class' associated function][`RefBifunctor::ref_map_first`].
256	#[document_signature]
257	///
258	#[document_type_parameters(
259		"The lifetime of the values.",
260		"The brand of the bifunctor.",
261		"The type of the first value.",
262		"The type of the first result.",
263		"The type of the second value (must be Clone)."
264	)]
265	///
266	#[document_parameters(
267		"The function to apply to the first value.",
268		"The bifunctor instance (borrowed)."
269	)]
270	///
271	#[document_returns("A new bifunctor instance with the first value transformed.")]
272	#[document_examples]
273	///
274	/// ```
275	/// use fp_library::{
276	/// 	brands::*,
277	/// 	classes::ref_bifunctor::*,
278	/// };
279	///
280	/// let x = Result::<i32, i32>::Err(5);
281	/// let y = ref_map_first::<ResultBrand, _, _, _>(|e: &i32| *e * 2, &x);
282	/// assert_eq!(y, Err(10));
283	/// ```
284	pub fn ref_map_first<'a, Brand: RefBifunctor, A: 'a, B: 'a, C: Clone + 'a>(
285		f: impl Fn(&A) -> B + 'a,
286		p: &Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>),
287	) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, B, C>) {
288		Brand::ref_map_first(f, p)
289	}
290
291	/// Maps a function over the second type argument of the bifunctor by reference.
292	///
293	/// Free function version that dispatches to [the type class' associated function][`RefBifunctor::ref_map_second`].
294	#[document_signature]
295	///
296	#[document_type_parameters(
297		"The lifetime of the values.",
298		"The brand of the bifunctor.",
299		"The type of the first value (must be Clone).",
300		"The type of the second value.",
301		"The type of the second result."
302	)]
303	///
304	#[document_parameters(
305		"The function to apply to the second value.",
306		"The bifunctor instance (borrowed)."
307	)]
308	///
309	#[document_returns("A new bifunctor instance with the second value transformed.")]
310	#[document_examples]
311	///
312	/// ```
313	/// use fp_library::{
314	/// 	brands::*,
315	/// 	classes::ref_bifunctor::*,
316	/// };
317	///
318	/// let x = Result::<i32, i32>::Ok(5);
319	/// let y = ref_map_second::<ResultBrand, _, _, _>(|s: &i32| *s * 2, &x);
320	/// assert_eq!(y, Ok(10));
321	/// ```
322	pub fn ref_map_second<'a, Brand: RefBifunctor, A: Clone + 'a, B: 'a, C: 'a>(
323		g: impl Fn(&B) -> C + 'a,
324		p: &Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, B>),
325	) -> Apply!(<Brand as Kind!( type Of<'a, A: 'a, B: 'a>: 'a; )>::Of<'a, A, C>) {
326		Brand::ref_map_second(g, p)
327	}
328
329	/// [`RefFunctor`] instance for [`BifunctorFirstAppliedBrand`].
330	///
331	/// Maps over the first type parameter of a bifunctor by reference, delegating to
332	/// [`RefBifunctor::ref_bimap`] with [`Clone::clone`] for the second argument.
333	/// Requires `Clone` on the fixed second type parameter because the value must be
334	/// cloned out of the borrowed container.
335	#[document_type_parameters("The bifunctor brand.", "The fixed second type parameter.")]
336	impl<Brand: Bifunctor + RefBifunctor, A: Clone + 'static> RefFunctor
337		for BifunctorFirstAppliedBrand<Brand, A>
338	{
339		/// Maps a function over the first type parameter by reference.
340		#[document_signature]
341		#[document_type_parameters(
342			"The lifetime of the values.",
343			"The input type.",
344			"The output type."
345		)]
346		#[document_parameters("The function to apply.", "The bifunctor value to map over.")]
347		#[document_returns("The mapped bifunctor value.")]
348		#[document_examples]
349		///
350		/// ```
351		/// use fp_library::{
352		/// 	brands::*,
353		/// 	functions::explicit::*,
354		/// };
355		///
356		/// let x = Result::<i32, i32>::Ok(5);
357		/// let y = map::<BifunctorFirstAppliedBrand<ResultBrand, i32>, _, _, _, _>(|s: &i32| *s * 2, &x);
358		/// assert_eq!(y, Ok(10));
359		/// ```
360		fn ref_map<'a, B: 'a, C: 'a>(
361			func: impl Fn(&B) -> C + 'a,
362			fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
363		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) {
364			Brand::ref_bimap(|a: &A| a.clone(), func, fa)
365		}
366	}
367
368	/// [`RefFunctor`] instance for [`BifunctorSecondAppliedBrand`].
369	///
370	/// Maps over the second type parameter of a bifunctor by reference, delegating to
371	/// [`RefBifunctor::ref_bimap`] with [`Clone::clone`] for the first argument.
372	/// Requires `Clone` on the fixed first type parameter because the value must be
373	/// cloned out of the borrowed container.
374	#[document_type_parameters("The bifunctor brand.", "The fixed first type parameter.")]
375	impl<Brand: Bifunctor + RefBifunctor, B: Clone + 'static> RefFunctor
376		for BifunctorSecondAppliedBrand<Brand, B>
377	{
378		/// Maps a function over the second type parameter by reference.
379		#[document_signature]
380		#[document_type_parameters(
381			"The lifetime of the values.",
382			"The input type.",
383			"The output type."
384		)]
385		#[document_parameters("The function to apply.", "The bifunctor value to map over.")]
386		#[document_returns("The mapped bifunctor value.")]
387		#[document_examples]
388		///
389		/// ```
390		/// use fp_library::{
391		/// 	brands::*,
392		/// 	functions::explicit::*,
393		/// };
394		///
395		/// let x = Result::<i32, i32>::Err(5);
396		/// let y = map::<BifunctorSecondAppliedBrand<ResultBrand, i32>, _, _, _, _>(|e: &i32| *e * 2, &x);
397		/// assert_eq!(y, Err(10));
398		/// ```
399		fn ref_map<'a, A: 'a, C: 'a>(
400			func: impl Fn(&A) -> C + 'a,
401			fa: &Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
402		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>) {
403			Brand::ref_bimap(func, |b: &B| b.clone(), fa)
404		}
405	}
406}
407
408pub use inner::*;
409
410#[cfg(test)]
411mod tests {
412	use {
413		crate::{
414			brands::*,
415			functions::explicit::*,
416		},
417		quickcheck_macros::quickcheck,
418	};
419
420	/// RefBifunctor identity law: bimap((Clone::clone, Clone::clone), &p) == p.
421	#[quickcheck]
422	fn prop_ref_bifunctor_identity(
423		a: i32,
424		c: i32,
425	) -> bool {
426		let ok: Result<i32, i32> = Ok(c);
427		let err: Result<i32, i32> = Err(a);
428		bimap::<ResultBrand, _, _, _, _, _, _>((|x: &i32| *x, |x: &i32| *x), &ok) == ok
429			&& bimap::<ResultBrand, _, _, _, _, _, _>((|x: &i32| *x, |x: &i32| *x), &err) == err
430	}
431
432	/// RefBifunctor composition law.
433	#[quickcheck]
434	fn prop_ref_bifunctor_composition(
435		a: i32,
436		c: i32,
437	) -> bool {
438		let f1 = |x: &i32| x.wrapping_add(1);
439		let f2 = |x: &i32| x.wrapping_mul(2);
440		let g1 = |x: &i32| x.wrapping_add(10);
441		let g2 = |x: &i32| x.wrapping_mul(3);
442
443		let ok: Result<i32, i32> = Ok(c);
444		let err: Result<i32, i32> = Err(a);
445
446		let composed_ok = bimap::<ResultBrand, _, _, _, _, _, _>(
447			(|x: &i32| f2(&f1(x)), |x: &i32| g2(&g1(x))),
448			&ok,
449		);
450		let sequential_ok = bimap::<ResultBrand, _, _, _, _, _, _>(
451			(f2, g2),
452			&bimap::<ResultBrand, _, _, _, _, _, _>((f1, g1), &ok),
453		);
454
455		let composed_err = bimap::<ResultBrand, _, _, _, _, _, _>(
456			(|x: &i32| f2(&f1(x)), |x: &i32| g2(&g1(x))),
457			&err,
458		);
459		let sequential_err = bimap::<ResultBrand, _, _, _, _, _, _>(
460			(f2, g2),
461			&bimap::<ResultBrand, _, _, _, _, _, _>((f1, g1), &err),
462		);
463
464		composed_ok == sequential_ok && composed_err == sequential_err
465	}
466}