fp_library/classes/
semigroup.rs

1use crate::{
2	classes::{ClonableFn, clonable_fn::ApplyFn},
3	hkt::{Apply1L0T, Kind1L0T},
4};
5
6/// A type class for semigroups.
7///
8/// A `Semigroup` is a set equipped with an associative binary operation.
9///
10/// In functional programming, semigroups are useful for combining values
11/// in a consistent way. They form the basis for more complex structures
12/// like monoids.
13///
14/// # Laws
15///
16/// Semigroup instances must satisfy the associative law:
17/// * Associativity: `append(append(a)(b))(c) = append(a)(append(b)(c))`.
18pub trait Semigroup<'b> {
19	/// Associative operation that combines two values of the same type.
20	///
21	/// # Type Signature
22	///
23	/// `Semigroup a => a -> a -> a`
24	///
25	/// # Parameters
26	///
27	/// * `a`: First value to combine.
28	/// * `b`: Second value to combine.
29	///
30	/// # Returns
31	///
32	/// The result of combining the two values using the semigroup operation.
33	fn append<'a, ClonableFnBrand: 'a + 'b + ClonableFn>(
34		a: Self
35	) -> ApplyFn<'a, ClonableFnBrand, Self, Self>
36	where
37		Self: Sized,
38		'b: 'a;
39}
40
41/// A higher-kinded Semigroup, abstracting over the lifetime parameter.
42pub trait Semigroup1L0T: Kind1L0T
43where
44	for<'a> Apply1L0T<'a, Self>: Semigroup<'a>,
45{
46}
47
48/// Associative operation that combines two values of the same type.
49///
50/// Free function version that dispatches to [the type class' associated function][`Semigroup::append`].
51///
52/// # Type Signature
53///
54/// `Semigroup a => a -> a -> a`
55///
56/// # Parameters
57///
58/// * `a`: First value to combine.
59/// * `b`: Second value to combine.
60///
61/// # Returns
62///
63/// The result of combining the two values using the semigroup operation.
64///
65/// # Examples
66///
67/// ```
68/// use fp_library::{brands::RcFnBrand, functions::append};
69///
70/// assert_eq!(
71///     append::<RcFnBrand, String>("Hello, ".to_string())("World!".to_string()),
72///     "Hello, World!"
73/// );
74/// ```
75pub fn append<'a, ClonableFnBrand: 'a + ClonableFn, Brand: Semigroup1L0T>(
76	a: Apply1L0T<'a, Brand>
77) -> ApplyFn<'a, ClonableFnBrand, Apply1L0T<'a, Brand>, Apply1L0T<'a, Brand>>
78where
79	for<'b> Apply1L0T<'b, Brand>: Semigroup<'b>,
80{
81	<Apply1L0T<'a, Brand> as Semigroup<'a>>::append::<ClonableFnBrand>(a)
82}
83
84// #[cfg(test)]
85// mod tests {
86// 	use crate::{brands::RcFnBrand, functions::append};
87
88// 	#[test]
89// 	fn test_string_semigroup() {
90// 		let s1 = "Hello, ".to_string();
91// 		let s2 = "World!".to_string();
92// 		assert_eq!(append::<String, RcFnBrand>(s1)(s2), "Hello, World!");
93// 	}
94
95// 	#[test]
96// 	fn test_string_semigroup_associativity() {
97// 		let s1 = "a".to_string();
98// 		let s2 = "b".to_string();
99// 		let s3 = "c".to_string();
100
101// 		// (a <> b) <> c = a <> (b <> c)
102// 		let left_associated =
103// 			append::<String, RcFnBrand>(append::<String, RcFnBrand>(s1.clone())(s2.clone()))(s3.clone());
104// 		let right_associated =
105// 			append::<String, RcFnBrand>(s1.clone())(append::<String, RcFnBrand>(s2.clone())(s3.clone()));
106
107// 		assert_eq!(left_associated, right_associated);
108// 		assert_eq!(left_associated, "abc");
109// 	}
110// }