Skip to main content

acls_rs/algebra/
monoid.rs

1//! Monoid algebraic structure.
2//!
3//! A monoid is a semigroup with an identity element.
4//!
5//! # Algebraic Laws
6//!
7//! For a type to implement `Monoid`, it must satisfy:
8//!
9//! - **Associativity**: `(a ∘ b) ∘ c = a ∘ (b ∘ c)` (inherited from Semigroup)
10//! - **Left Identity**: `e ∘ a = a` for all `a`
11//! - **Right Identity**: `a ∘ e = a` for all `a`
12//!
13//! where `e` is the identity element.
14
15use super::Semigroup;
16use std::collections::BTreeSet;
17
18/// A monoid: a semigroup with an identity element.
19///
20/// # Laws
21///
22/// Implementations must satisfy (in addition to semigroup laws):
23/// - **Left Identity**: `Monoid::identity().combine(a) == a`
24/// - **Right Identity**: `a.combine(Monoid::identity()) == a`
25pub trait Monoid: Semigroup {
26    /// Return the identity element.
27    ///
28    /// The identity element must satisfy:
29    /// - `identity().combine(a) == a` (left identity)
30    /// - `a.combine(identity()) == a` (right identity)
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use acls_rs::algebra::Monoid;
36    /// use acls_rs::permission::PermissionSet;
37    ///
38    /// let empty = PermissionSet::identity();
39    /// assert!(empty.is_empty());
40    /// ```
41    fn identity() -> Self;
42
43    /// Combine all elements from an iterator, using identity for empty iterators.
44    ///
45    /// Unlike `Semigroup::combine_all`, this always returns a value, using the
46    /// identity element when the iterator is empty.
47    ///
48    /// # Examples
49    ///
50    /// ```
51    /// use acls_rs::algebra::Monoid;
52    /// use acls_rs::permission::PermissionSet;
53    ///
54    /// let empty: Vec<PermissionSet> = vec![];
55    /// let result = PermissionSet::combine_all_with_identity(empty);
56    /// assert_eq!(result, PermissionSet::identity());
57    /// ```
58    fn combine_all_with_identity<I: IntoIterator<Item = Self>>(iter: I) -> Self {
59        Self::combine_all(iter).unwrap_or_else(Self::identity)
60    }
61}
62
63// Blanket implementation for BTreeSet
64impl<T: Ord + Clone> Monoid for BTreeSet<T> {
65    #[inline]
66    fn identity() -> Self {
67        BTreeSet::new()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::algebra::Semigroup;
75
76    #[test]
77    fn test_btreeset_left_identity() {
78        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
79        let e = BTreeSet::identity();
80
81        assert_eq!(e.combine(a.clone()), a);
82    }
83
84    #[test]
85    fn test_btreeset_right_identity() {
86        let a: BTreeSet<i32> = [1, 2, 3].iter().cloned().collect();
87        let e = BTreeSet::identity();
88
89        assert_eq!(a.clone().combine(e), a);
90    }
91
92    #[test]
93    fn test_combine_all_with_identity_empty() {
94        let empty: Vec<BTreeSet<i32>> = vec![];
95        let result = BTreeSet::combine_all_with_identity(empty);
96
97        assert_eq!(result, BTreeSet::identity());
98    }
99
100    #[test]
101    fn test_combine_all_with_identity_nonempty() {
102        let sets = vec![
103            [1, 2].iter().cloned().collect::<BTreeSet<_>>(),
104            [3, 4].iter().cloned().collect::<BTreeSet<_>>(),
105        ];
106
107        let result = BTreeSet::combine_all_with_identity(sets);
108        assert_eq!(result, [1, 2, 3, 4].iter().cloned().collect());
109    }
110}