1pub trait Semigroup {
3 fn combine(self, other: Self) -> Self;
4}
5
6macro_rules! impl_additive_semigroup {
7 ($($t:ty),*) => {
8 $(
9 impl Semigroup for $t {
10 fn combine(self, other: Self) -> Self {
11 self + other
12 }
13 }
14 )*
15 };
16}
17
18impl_additive_semigroup!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
19
20#[cfg(any(feature = "std", feature = "alloc"))]
21impl Semigroup for String {
22 fn combine(mut self, other: Self) -> Self {
23 self.push_str(&other);
24 self
25 }
26}
27
28#[cfg(any(feature = "std", feature = "alloc"))]
29impl<T> Semigroup for Vec<T> {
30 fn combine(mut self, other: Self) -> Self {
31 self.extend(other);
32 self
33 }
34}
35
36impl<T: Semigroup> Semigroup for Option<T> {
37 fn combine(self, other: Self) -> Self {
38 match (self, other) {
39 (Some(a), Some(b)) => Some(a.combine(b)),
40 (a @ Some(_), None) => a,
41 (None, b) => b,
42 }
43 }
44}
45
46#[cfg(any(feature = "std", feature = "alloc"))]
47impl<T> Semigroup for crate::hkt::NonEmptyVec<T> {
48 fn combine(mut self, other: Self) -> Self {
49 self.tail.push(other.head);
50 self.tail.extend(other.tail);
51 self
52 }
53}
54
55impl<A: Semigroup, B: Semigroup> Semigroup for (A, B) {
56 fn combine(self, other: Self) -> Self {
57 (self.0.combine(other.0), self.1.combine(other.1))
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn i32_combine() {
67 assert_eq!(3i32.combine(4), 7);
68 }
69
70 #[test]
71 fn string_combine() {
72 assert_eq!(
73 "hello ".to_string().combine("world".to_string()),
74 "hello world"
75 );
76 }
77
78 #[test]
79 fn vec_combine() {
80 assert_eq!(vec![1, 2].combine(vec![3, 4]), vec![1, 2, 3, 4]);
81 }
82
83 #[test]
84 fn option_combine() {
85 assert_eq!(Some(3i32).combine(Some(4)), Some(7));
86 assert_eq!(Some(3i32).combine(None), Some(3));
87 assert_eq!(None::<i32>.combine(Some(4)), Some(4));
88 assert_eq!(None::<i32>.combine(None), None);
89 }
90}
91
92#[cfg(test)]
93mod law_tests {
94 use super::*;
95 use proptest::prelude::*;
96
97 proptest! {
98 #[test]
99 fn i32_associativity(a in any::<i32>(), b in any::<i32>(), c in any::<i32>()) {
100 let left = (a.wrapping_add(b)).wrapping_add(c);
102 let right = a.wrapping_add(b.wrapping_add(c));
103 prop_assert_eq!(left, right);
104 }
105
106 #[test]
107 fn string_associativity(
108 a in "[a-z]{0,10}",
109 b in "[a-z]{0,10}",
110 c in "[a-z]{0,10}"
111 ) {
112 let left = a.clone().combine(b.clone()).combine(c.clone());
113 let right = a.combine(b.combine(c));
114 prop_assert_eq!(left, right);
115 }
116
117 #[test]
118 fn vec_associativity(
119 a in prop::collection::vec(any::<i32>(), 0..5),
120 b in prop::collection::vec(any::<i32>(), 0..5),
121 c in prop::collection::vec(any::<i32>(), 0..5)
122 ) {
123 let left = a.clone().combine(b.clone()).combine(c.clone());
124 let right = a.combine(b.combine(c));
125 prop_assert_eq!(left, right);
126 }
127
128 #[test]
129 fn option_associativity(
130 a in proptest::option::of(0u16..100),
131 b in proptest::option::of(0u16..100),
132 c in proptest::option::of(0u16..100)
133 ) {
134 let left = a.clone().combine(b.clone()).combine(c.clone());
135 let right = a.combine(b.combine(c));
136 prop_assert_eq!(left, right);
137 }
138 }
139}