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}