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// }