1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#[allow(unused_imports)]
use alloc::{boxed::Box, format, string::String, string::ToString, vec, vec::Vec};
use core::fmt::Debug;
// Monoid — a set with an associative binary operation and identity element.
//
// The three monoid laws:
// 1. Associativity: combine(combine(a, b), c) = combine(a, combine(b, c))
// 2. Left identity: combine(empty(), a) = a
// 3. Right identity: combine(a, empty()) = a
//
// In pr4xis, monoids underlie:
// - Trace accumulation: (Vec<TraceRecord>, concat, [])
// - Morphism composition: (Morphisms, compose, identity)
// - String building: (String, +, "")
//
// References:
// - Mac Lane, "Categories for the Working Mathematician" (1971), Ch. VII
// - Haskell Data.Monoid — the standard formalization
/// A monoid: a type with an associative binary operation and identity element.
pub trait Monoid: Clone + Debug {
/// The identity element. For all a: combine(empty(), a) = a = combine(a, empty()).
fn empty() -> Self;
/// The associative binary operation. combine(combine(a, b), c) = combine(a, combine(b, c)).
fn combine(&self, other: &Self) -> Self;
}
// --- Standard monoid instances ---
/// `Vec<T>` is a monoid under concatenation.
impl<T: Clone + Debug> Monoid for Vec<T> {
fn empty() -> Self {
Vec::new()
}
fn combine(&self, other: &Self) -> Self {
let mut result = self.clone();
result.extend(other.iter().cloned());
result
}
}
/// String is a monoid under concatenation.
impl Monoid for String {
fn empty() -> Self {
String::new()
}
fn combine(&self, other: &Self) -> Self {
let mut result = self.clone();
result.push_str(other);
result
}
}
/// () is the trivial monoid.
impl Monoid for () {
fn empty() -> Self {}
fn combine(&self, _other: &Self) -> Self {}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn vec_monoid_identity() {
let v = vec![1, 2, 3];
assert_eq!(Vec::<i32>::empty().combine(&v), v);
assert_eq!(v.combine(&Vec::empty()), v);
}
#[test]
fn vec_monoid_associativity() {
let a = vec![1];
let b = vec![2];
let c = vec![3];
assert_eq!(a.combine(&b).combine(&c), a.combine(&b.combine(&c)));
}
#[test]
fn string_monoid_identity() {
let s = "hello".to_string();
assert_eq!(String::empty().combine(&s), s);
assert_eq!(s.combine(&String::empty()), s);
}
#[test]
fn string_monoid_associativity() {
let a = "a".to_string();
let b = "b".to_string();
let c = "c".to_string();
assert_eq!(a.combine(&b).combine(&c), a.combine(&b.combine(&c)));
}
#[test]
fn unit_monoid() {
assert_eq!(().combine(&()), ());
}
mod prop {
use super::*;
use proptest::prelude::*;
proptest! {
/// Vec monoid: left identity for any vec
#[test]
fn prop_vec_left_identity(v in proptest::collection::vec(any::<i32>(), 0..10)) {
prop_assert_eq!(Vec::<i32>::empty().combine(&v), v);
}
/// Vec monoid: right identity for any vec
#[test]
fn prop_vec_right_identity(v in proptest::collection::vec(any::<i32>(), 0..10)) {
prop_assert_eq!(v.combine(&Vec::<i32>::empty()), v);
}
/// Vec monoid: associativity for any three vecs
#[test]
fn prop_vec_associativity(
a in proptest::collection::vec(any::<i32>(), 0..5),
b in proptest::collection::vec(any::<i32>(), 0..5),
c in proptest::collection::vec(any::<i32>(), 0..5),
) {
let ab_c = a.combine(&b).combine(&c);
let a_bc: Vec<i32> = b.combine(&c);
let a_bc = a.combine(&a_bc);
prop_assert_eq!(ab_c, a_bc);
}
/// String monoid: left identity
#[test]
fn prop_string_left_identity(s in ".*") {
prop_assert_eq!(String::empty().combine(&s), s);
}
/// String monoid: right identity
#[test]
fn prop_string_right_identity(s in ".*") {
prop_assert_eq!(s.combine(&String::empty()), s);
}
/// String monoid: associativity
#[test]
fn prop_string_associativity(a in ".*", b in ".*", c in ".*") {
prop_assert_eq!(
a.combine(&b).combine(&c),
a.combine(&b.combine(&c))
);
}
}
}
}