Skip to main content

fp_library/types/
tuple_1.rs

1//! Single-value tuple with [`Functor`](crate::classes::Functor), [`Applicative`](crate::classes::Applicative), [`Monad`](crate::classes::Monad), [`MonadRec`](crate::classes::MonadRec), [`Foldable`](crate::classes::Foldable), [`Traversable`](crate::classes::Traversable), and parallel folding instances.
2//!
3//! A trivial wrapper using the native Rust 1-tuple `(A,)`.
4
5#[fp_macros::document_module]
6mod inner {
7	use {
8		crate::{
9			Apply,
10			brands::Tuple1Brand,
11			classes::{
12				Applicative,
13				ApplyFirst,
14				ApplySecond,
15				CloneableFn,
16				Foldable,
17				Functor,
18				Lift,
19				MonadRec,
20				Monoid,
21				Pointed,
22				Semiapplicative,
23				Semimonad,
24				Traversable,
25			},
26			impl_kind,
27			kinds::*,
28		},
29		core::ops::ControlFlow,
30		fp_macros::*,
31	};
32
33	impl_kind! {
34		for Tuple1Brand {
35			type Of<A> = (A,);
36		}
37	}
38
39	impl_kind! {
40		for Tuple1Brand {
41			type Of<'a, A: 'a>: 'a = (A,);
42		}
43	}
44
45	impl Functor for Tuple1Brand {
46		/// Maps a function over the value in the tuple.
47		///
48		/// This method applies a function to the value inside the 1-tuple, producing a new 1-tuple with the transformed value.
49		#[document_signature]
50		///
51		#[document_type_parameters(
52			"The lifetime of the value.",
53			"The type of the value inside the tuple.",
54			"The type of the result of applying the function."
55		)]
56		///
57		#[document_parameters("The function to apply.", "The tuple to map over.")]
58		///
59		#[document_returns("A new 1-tuple containing the result of applying the function.")]
60		///
61		#[document_examples]
62		///
63		/// ```
64		/// use fp_library::{
65		/// 	brands::*,
66		/// 	functions::*,
67		/// };
68		///
69		/// let x = (5,);
70		/// let y = map::<Tuple1Brand, _, _>(|i| i * 2, x);
71		/// assert_eq!(y, (10,));
72		/// ```
73		fn map<'a, A: 'a, B: 'a>(
74			func: impl Fn(A) -> B + 'a,
75			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
76		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
77			(func(fa.0),)
78		}
79	}
80
81	impl Lift for Tuple1Brand {
82		/// Lifts a binary function into the tuple context.
83		///
84		/// This method lifts a binary function to operate on values within the 1-tuple context.
85		#[document_signature]
86		///
87		#[document_type_parameters(
88			"The lifetime of the values.",
89			"The type of the first tuple's value.",
90			"The type of the second tuple's value.",
91			"The return type of the function."
92		)]
93		///
94		#[document_parameters(
95			"The binary function to apply.",
96			"The first tuple.",
97			"The second tuple."
98		)]
99		///
100		#[document_returns("A new 1-tuple containing the result of applying the function.")]
101		#[document_examples]
102		///
103		/// ```
104		/// use fp_library::{
105		/// 	brands::*,
106		/// 	functions::*,
107		/// };
108		///
109		/// let x = (1,);
110		/// let y = (2,);
111		/// let z = lift2::<Tuple1Brand, _, _, _>(|a, b| a + b, x, y);
112		/// assert_eq!(z, (3,));
113		/// ```
114		fn lift2<'a, A, B, C>(
115			func: impl Fn(A, B) -> C + 'a,
116			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
117			fb: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>),
118		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, C>)
119		where
120			A: 'a,
121			B: 'a,
122			C: 'a, {
123			(func(fa.0, fb.0),)
124		}
125	}
126
127	impl Pointed for Tuple1Brand {
128		/// Wraps a value in a 1-tuple.
129		///
130		/// This method wraps a value in a 1-tuple context.
131		#[document_signature]
132		///
133		#[document_type_parameters("The lifetime of the value.", "The type of the value to wrap.")]
134		///
135		#[document_parameters("The value to wrap.")]
136		///
137		#[document_returns("A 1-tuple containing the value.")]
138		///
139		#[document_examples]
140		///
141		/// ```
142		/// use fp_library::{
143		/// 	brands::*,
144		/// 	functions::*,
145		/// };
146		///
147		/// let x = pure::<Tuple1Brand, _>(5);
148		/// assert_eq!(x, (5,));
149		/// ```
150		fn pure<'a, A: 'a>(a: A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>) {
151			(a,)
152		}
153	}
154
155	impl ApplyFirst for Tuple1Brand {}
156	impl ApplySecond for Tuple1Brand {}
157
158	impl Semiapplicative for Tuple1Brand {
159		/// Applies a wrapped function to a wrapped value.
160		///
161		/// This method applies a function wrapped in a 1-tuple to a value wrapped in a 1-tuple.
162		#[document_signature]
163		///
164		#[document_type_parameters(
165			"The lifetime of the values.",
166			"The brand of the cloneable function wrapper.",
167			"The type of the input value.",
168			"The type of the output value."
169		)]
170		///
171		#[document_parameters(
172			"The tuple containing the function.",
173			"The tuple containing the value."
174		)]
175		///
176		#[document_returns("A new 1-tuple containing the result of applying the function.")]
177		#[document_examples]
178		///
179		/// ```
180		/// use fp_library::{
181		/// 	brands::*,
182		/// 	functions::*,
183		/// };
184		///
185		/// let f = (cloneable_fn_new::<RcFnBrand, _, _>(|x: i32| x * 2),);
186		/// let x = (5,);
187		/// let y = apply::<RcFnBrand, Tuple1Brand, _, _>(f, x);
188		/// assert_eq!(y, (10,));
189		/// ```
190		fn apply<'a, FnBrand: 'a + CloneableFn, A: 'a + Clone, B: 'a>(
191			ff: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, <FnBrand as CloneableFn>::Of<'a, A, B>>),
192			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
193		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
194			(ff.0(fa.0),)
195		}
196	}
197
198	impl Semimonad for Tuple1Brand {
199		/// Chains 1-tuple computations.
200		///
201		/// This method chains two 1-tuple computations, where the second computation depends on the result of the first.
202		#[document_signature]
203		///
204		#[document_type_parameters(
205			"The lifetime of the values.",
206			"The type of the result of the first computation.",
207			"The type of the result of the second computation."
208		)]
209		///
210		#[document_parameters(
211			"The first tuple.",
212			"The function to apply to the value inside the tuple."
213		)]
214		///
215		#[document_returns("The result of applying `f` to the value.")]
216		#[document_examples]
217		///
218		/// ```
219		/// use fp_library::{
220		/// 	brands::*,
221		/// 	functions::*,
222		/// };
223		///
224		/// let x = (5,);
225		/// let y = bind::<Tuple1Brand, _, _>(x, |i| (i * 2,));
226		/// assert_eq!(y, (10,));
227		/// ```
228		fn bind<'a, A: 'a, B: 'a>(
229			ma: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
230			func: impl Fn(A) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
231		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
232			func(ma.0)
233		}
234	}
235
236	impl Foldable for Tuple1Brand {
237		/// Folds the 1-tuple from the right.
238		///
239		/// This method performs a right-associative fold of the 1-tuple. Since it contains only one element, this is equivalent to applying the function to the element and the initial value.
240		#[document_signature]
241		///
242		#[document_type_parameters(
243			"The lifetime of the values.",
244			"The brand of the cloneable function to use.",
245			"The type of the elements in the structure.",
246			"The type of the accumulator."
247		)]
248		///
249		#[document_parameters(
250			"The function to apply to each element and the accumulator.",
251			"The initial value of the accumulator.",
252			"The tuple to fold."
253		)]
254		///
255		#[document_returns("The final accumulator value.")]
256		#[document_examples]
257		///
258		/// ```
259		/// use fp_library::{
260		/// 	brands::*,
261		/// 	functions::*,
262		/// };
263		///
264		/// let x = (5,);
265		/// let y = fold_right::<RcFnBrand, Tuple1Brand, _, _>(|a, b| a + b, 10, x);
266		/// assert_eq!(y, 15);
267		/// ```
268		fn fold_right<'a, FnBrand, A: 'a + Clone, B: 'a>(
269			func: impl Fn(A, B) -> B + 'a,
270			initial: B,
271			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
272		) -> B
273		where
274			FnBrand: CloneableFn + 'a, {
275			func(fa.0, initial)
276		}
277
278		/// Folds the 1-tuple from the left.
279		///
280		/// This method performs a left-associative fold of the 1-tuple. Since it contains only one element, this is equivalent to applying the function to the initial value and the element.
281		#[document_signature]
282		///
283		#[document_type_parameters(
284			"The lifetime of the values.",
285			"The brand of the cloneable function to use.",
286			"The type of the elements in the structure.",
287			"The type of the accumulator."
288		)]
289		///
290		#[document_parameters(
291			"The function to apply to the accumulator and each element.",
292			"The initial value of the accumulator.",
293			"The tuple to fold."
294		)]
295		///
296		#[document_returns("The final accumulator value.")]
297		#[document_examples]
298		///
299		/// ```
300		/// use fp_library::{
301		/// 	brands::*,
302		/// 	functions::*,
303		/// };
304		///
305		/// let x = (5,);
306		/// let y = fold_left::<RcFnBrand, Tuple1Brand, _, _>(|b, a| b + a, 10, x);
307		/// assert_eq!(y, 15);
308		/// ```
309		fn fold_left<'a, FnBrand, A: 'a + Clone, B: 'a>(
310			func: impl Fn(B, A) -> B + 'a,
311			initial: B,
312			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
313		) -> B
314		where
315			FnBrand: CloneableFn + 'a, {
316			func(initial, fa.0)
317		}
318
319		/// Maps the value to a monoid and returns it.
320		///
321		/// This method maps the element of the 1-tuple to a monoid.
322		#[document_signature]
323		///
324		#[document_type_parameters(
325			"The lifetime of the values.",
326			"The brand of the cloneable function to use.",
327			"The type of the elements in the structure.",
328			"The type of the monoid."
329		)]
330		///
331		#[document_parameters(
332			"The thread-safe function to map each element to a monoid.",
333			"The tuple to fold."
334		)]
335		///
336		#[document_returns("The monoid value.")]
337		#[document_examples]
338		///
339		/// ```
340		/// use fp_library::{
341		/// 	brands::*,
342		/// 	functions::*,
343		/// };
344		///
345		/// let x = (5,);
346		/// let y = fold_map::<RcFnBrand, Tuple1Brand, _, _>(|a: i32| a.to_string(), x);
347		/// assert_eq!(y, "5".to_string());
348		/// ```
349		fn fold_map<'a, FnBrand, A: 'a + Clone, M>(
350			func: impl Fn(A) -> M + 'a,
351			fa: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
352		) -> M
353		where
354			M: Monoid + 'a,
355			FnBrand: CloneableFn + 'a, {
356			func(fa.0)
357		}
358	}
359
360	impl Traversable for Tuple1Brand {
361		/// Traverses the 1-tuple with an applicative function.
362		///
363		/// This method maps the element of the 1-tuple to a computation, evaluates it, and wraps the result in the applicative context.
364		#[document_signature]
365		///
366		#[document_type_parameters(
367			"The lifetime of the values.",
368			"The type of the elements in the traversable structure.",
369			"The type of the elements in the resulting traversable structure.",
370			"The applicative context."
371		)]
372		///
373		#[document_parameters(
374			"The function to apply to each element, returning a value in an applicative context.",
375			"The tuple to traverse."
376		)]
377		///
378		#[document_returns("The 1-tuple wrapped in the applicative context.")]
379		#[document_examples]
380		///
381		/// ```
382		/// use fp_library::{
383		/// 	brands::*,
384		/// 	functions::*,
385		/// };
386		///
387		/// let x = (5,);
388		/// let y = traverse::<Tuple1Brand, _, _, OptionBrand>(|a| Some(a * 2), x);
389		/// assert_eq!(y, Some((10,)));
390		/// ```
391		fn traverse<'a, A: 'a + Clone, B: 'a + Clone, F: Applicative>(
392			func: impl Fn(A) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) + 'a,
393			ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>),
394		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>)>)
395		where
396			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>): Clone, {
397			F::map(|b| (b,), func(ta.0))
398		}
399
400		/// Sequences a 1-tuple of applicative.
401		///
402		/// This method evaluates the computation inside the 1-tuple and wraps the result in the applicative context.
403		#[document_signature]
404		///
405		#[document_type_parameters(
406			"The lifetime of the values.",
407			"The type of the elements in the traversable structure.",
408			"The applicative context."
409		)]
410		///
411		#[document_parameters("The tuple containing the applicative value.")]
412		///
413		#[document_returns("The 1-tuple wrapped in the applicative context.")]
414		///
415		#[document_examples]
416		///
417		/// ```
418		/// use fp_library::{
419		/// 	brands::*,
420		/// 	functions::*,
421		/// };
422		///
423		/// let x = (Some(5),);
424		/// let y = sequence::<Tuple1Brand, _, OptionBrand>(x);
425		/// assert_eq!(y, Some((5,)));
426		/// ```
427		fn sequence<'a, A: 'a + Clone, F: Applicative>(
428			ta: Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)>)
429		) -> Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>)>)
430		where
431			Apply!(<F as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone,
432			Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, A>): Clone, {
433			F::map(|a| (a,), ta.0)
434		}
435	}
436
437	impl MonadRec for Tuple1Brand {
438		/// Performs tail-recursive monadic computation over 1-tuples.
439		///
440		/// Since the 1-tuple has no effect, this simply loops on the inner value
441		/// until the step function returns [`ControlFlow::Break`].
442		#[document_signature]
443		///
444		#[document_type_parameters(
445			"The lifetime of the computation.",
446			"The type of the initial value and loop state.",
447			"The type of the result."
448		)]
449		///
450		#[document_parameters("The step function.", "The initial value.")]
451		///
452		#[document_returns("A 1-tuple containing the result of the computation.")]
453		///
454		#[document_examples]
455		///
456		/// ```
457		/// use {
458		/// 	core::ops::ControlFlow,
459		/// 	fp_library::{
460		/// 		brands::*,
461		/// 		functions::*,
462		/// 		types::*,
463		/// 	},
464		/// };
465		///
466		/// let result = tail_rec_m::<Tuple1Brand, _, _>(
467		/// 	|n| {
468		/// 		if n < 10 { (ControlFlow::Continue(n + 1),) } else { (ControlFlow::Break(n),) }
469		/// 	},
470		/// 	0,
471		/// );
472		/// assert_eq!(result, (10,));
473		/// ```
474		fn tail_rec_m<'a, A: 'a, B: 'a>(
475			func: impl Fn(
476				A,
477			)
478				-> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, ControlFlow<B, A>>)
479			+ 'a,
480			initial: A,
481		) -> Apply!(<Self as Kind!( type Of<'a, T: 'a>: 'a; )>::Of<'a, B>) {
482			let mut current = initial;
483			loop {
484				match func(current).0 {
485					ControlFlow::Continue(next) => current = next,
486					ControlFlow::Break(b) => return (b,),
487				}
488			}
489		}
490	}
491}
492
493#[cfg(test)]
494mod tests {
495
496	use {
497		crate::{
498			brands::{
499				OptionBrand,
500				RcFnBrand,
501				Tuple1Brand,
502			},
503			classes::{
504				CloneableFn,
505				functor::map,
506				pointed::pure,
507				semiapplicative::apply,
508				semimonad::bind,
509			},
510			functions::{
511				compose,
512				identity,
513			},
514		},
515		quickcheck_macros::quickcheck,
516	};
517
518	// Functor Laws
519
520	/// Tests the identity law for Functor.
521	#[quickcheck]
522	fn functor_identity(x: i32) -> bool {
523		let x = (x,);
524		map::<Tuple1Brand, _, _>(identity, x) == x
525	}
526
527	/// Tests the composition law for Functor.
528	#[quickcheck]
529	fn functor_composition(x: i32) -> bool {
530		let x = (x,);
531		let f = |x: i32| x.wrapping_add(1);
532		let g = |x: i32| x.wrapping_mul(2);
533		map::<Tuple1Brand, _, _>(compose(f, g), x)
534			== map::<Tuple1Brand, _, _>(f, map::<Tuple1Brand, _, _>(g, x))
535	}
536
537	// Applicative Laws
538
539	/// Tests the identity law for Applicative.
540	#[quickcheck]
541	fn applicative_identity(v: i32) -> bool {
542		let v = (v,);
543		apply::<RcFnBrand, Tuple1Brand, _, _>(
544			pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(identity)),
545			v,
546		) == v
547	}
548
549	/// Tests the homomorphism law for Applicative.
550	#[quickcheck]
551	fn applicative_homomorphism(x: i32) -> bool {
552		let f = |x: i32| x.wrapping_mul(2);
553		apply::<RcFnBrand, Tuple1Brand, _, _>(
554			pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(f)),
555			pure::<Tuple1Brand, _>(x),
556		) == pure::<Tuple1Brand, _>(f(x))
557	}
558
559	/// Tests the composition law for Applicative.
560	#[quickcheck]
561	fn applicative_composition(
562		w: i32,
563		u_val: i32,
564		v_val: i32,
565	) -> bool {
566		let w = (w,);
567		let v_fn = move |x: i32| x.wrapping_mul(v_val);
568		let u_fn = move |x: i32| x.wrapping_add(u_val);
569
570		let v = pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(v_fn));
571		let u = pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(u_fn));
572
573		// RHS: u <*> (v <*> w)
574		let vw = apply::<RcFnBrand, Tuple1Brand, _, _>(v.clone(), w);
575		let rhs = apply::<RcFnBrand, Tuple1Brand, _, _>(u.clone(), vw);
576
577		// LHS: pure(compose) <*> u <*> v <*> w
578		let composed = move |x| u_fn(v_fn(x));
579		let uv = pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(composed));
580
581		let lhs = apply::<RcFnBrand, Tuple1Brand, _, _>(uv, w);
582
583		lhs == rhs
584	}
585
586	/// Tests the interchange law for Applicative.
587	#[quickcheck]
588	fn applicative_interchange(y: i32) -> bool {
589		// u <*> pure y = pure ($ y) <*> u
590		let f = |x: i32| x.wrapping_mul(2);
591		let u = pure::<Tuple1Brand, _>(<RcFnBrand as CloneableFn>::new(f));
592
593		let lhs = apply::<RcFnBrand, Tuple1Brand, _, _>(u.clone(), pure::<Tuple1Brand, _>(y));
594
595		let rhs_fn =
596			<RcFnBrand as CloneableFn>::new(move |f: std::rc::Rc<dyn Fn(i32) -> i32>| f(y));
597		let rhs = apply::<RcFnBrand, Tuple1Brand, _, _>(pure::<Tuple1Brand, _>(rhs_fn), u);
598
599		lhs == rhs
600	}
601
602	// Monad Laws
603
604	/// Tests the left identity law for Monad.
605	#[quickcheck]
606	fn monad_left_identity(a: i32) -> bool {
607		let f = |x: i32| (x.wrapping_mul(2),);
608		bind::<Tuple1Brand, _, _>(pure::<Tuple1Brand, _>(a), f) == f(a)
609	}
610
611	/// Tests the right identity law for Monad.
612	#[quickcheck]
613	fn monad_right_identity(m: i32) -> bool {
614		let m = (m,);
615		bind::<Tuple1Brand, _, _>(m, pure::<Tuple1Brand, _>) == m
616	}
617
618	/// Tests the associativity law for Monad.
619	#[quickcheck]
620	fn monad_associativity(m: i32) -> bool {
621		let m = (m,);
622		let f = |x: i32| (x.wrapping_mul(2),);
623		let g = |x: i32| (x.wrapping_add(1),);
624		bind::<Tuple1Brand, _, _>(bind::<Tuple1Brand, _, _>(m, f), g)
625			== bind::<Tuple1Brand, _, _>(m, |x| bind::<Tuple1Brand, _, _>(f(x), g))
626	}
627
628	// Edge Cases
629
630	/// Tests the `map` function.
631	#[test]
632	fn map_test() {
633		assert_eq!(map::<Tuple1Brand, _, _>(|x: i32| x + 1, (1,)), (2,));
634	}
635
636	/// Tests the `bind` function.
637	#[test]
638	fn bind_test() {
639		assert_eq!(bind::<Tuple1Brand, _, _>((1,), |x| (x + 1,)), (2,));
640	}
641
642	/// Tests the `fold_right` function.
643	#[test]
644	fn fold_right_test() {
645		assert_eq!(
646			crate::classes::foldable::fold_right::<RcFnBrand, Tuple1Brand, _, _>(
647				|x: i32, acc| x + acc,
648				0,
649				(1,)
650			),
651			1
652		);
653	}
654
655	/// Tests the `fold_left` function.
656	#[test]
657	fn fold_left_test() {
658		assert_eq!(
659			crate::classes::foldable::fold_left::<RcFnBrand, Tuple1Brand, _, _>(
660				|acc, x: i32| acc + x,
661				0,
662				(1,)
663			),
664			1
665		);
666	}
667
668	/// Tests the `traverse` function.
669	#[test]
670	fn traverse_test() {
671		assert_eq!(
672			crate::classes::traversable::traverse::<Tuple1Brand, _, _, OptionBrand>(
673				|x: i32| Some(x + 1),
674				(1,)
675			),
676			Some((2,))
677		);
678	}
679
680	// MonadRec tests
681
682	/// Tests the MonadRec identity law: `tail_rec_m(|a| pure(Done(a)), x) == pure(x)`.
683	#[quickcheck]
684	fn monad_rec_identity(x: i32) -> bool {
685		use {
686			crate::classes::monad_rec::tail_rec_m,
687			core::ops::ControlFlow,
688		};
689		tail_rec_m::<Tuple1Brand, _, _>(|a| (ControlFlow::Break(a),), x) == (x,)
690	}
691
692	/// Tests a recursive computation that sums a range via `tail_rec_m`.
693	#[test]
694	fn monad_rec_sum_range() {
695		use {
696			crate::classes::monad_rec::tail_rec_m,
697			core::ops::ControlFlow,
698		};
699		let result = tail_rec_m::<Tuple1Brand, _, _>(
700			|(n, acc)| {
701				if n == 0 {
702					(ControlFlow::Break(acc),)
703				} else {
704					(ControlFlow::Continue((n - 1, acc + n)),)
705				}
706			},
707			(100i64, 0i64),
708		);
709		assert_eq!(result, (5050,));
710	}
711
712	/// Tests stack safety: `tail_rec_m` handles large iteration counts.
713	#[test]
714	fn monad_rec_stack_safety() {
715		use {
716			crate::classes::monad_rec::tail_rec_m,
717			core::ops::ControlFlow,
718		};
719		let iterations: i64 = 200_000;
720		let result = tail_rec_m::<Tuple1Brand, _, _>(
721			|acc| {
722				if acc < iterations {
723					(ControlFlow::Continue(acc + 1),)
724				} else {
725					(ControlFlow::Break(acc),)
726				}
727			},
728			0i64,
729		);
730		assert_eq!(result, (iterations,));
731	}
732}