fp_library/typeclasses/
semigroup.rs

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