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