Skip to main content

karpal_core/
monad.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::applicative::Applicative;
5use crate::chain::Chain;
6
7/// Monad: Applicative + Chain with no extra methods (blanket impl).
8///
9/// Laws:
10/// - Left identity: `chain(pure(a), f) == f(a)`
11/// - Right identity: `chain(m, pure) == m`
12pub trait Monad: Applicative + Chain {}
13
14impl<F: Applicative + Chain> Monad for F {}
15
16#[cfg(test)]
17mod law_tests {
18    use crate::applicative::Applicative;
19    use crate::chain::Chain;
20    use crate::hkt::OptionF;
21    #[cfg(any(feature = "std", feature = "alloc"))]
22    use crate::hkt::VecF;
23    use proptest::prelude::*;
24
25    proptest! {
26        // Left identity: chain(pure(a), f) == f(a)
27        #[test]
28        fn option_left_identity(a in any::<i32>()) {
29            let f = |x: i32| Some(x.wrapping_mul(2));
30            let left = OptionF::chain(OptionF::pure(a), f);
31            let right = f(a);
32            prop_assert_eq!(left, right);
33        }
34
35        // Right identity: chain(m, pure) == m
36        #[test]
37        fn option_right_identity(m in any::<Option<i32>>()) {
38            let left = OptionF::chain(m, OptionF::pure);
39            prop_assert_eq!(left, m);
40        }
41
42        #[test]
43        fn vec_left_identity(a in any::<i32>()) {
44            let f = |x: i32| vec![x, x.wrapping_add(1)];
45            let left = VecF::chain(VecF::pure(a), f);
46            let right = f(a);
47            prop_assert_eq!(left, right);
48        }
49
50        #[test]
51        fn vec_right_identity(m in prop::collection::vec(any::<i32>(), 0..10)) {
52            let left = VecF::chain(m.clone(), VecF::pure);
53            prop_assert_eq!(left, m);
54        }
55    }
56}