Skip to main content

karpal_core/
plus.rs

1// Copyright (C) 2026 Industrial Algebra
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::alt::Alt;
5use crate::hkt::OptionF;
6#[cfg(any(feature = "std", feature = "alloc"))]
7use crate::hkt::VecF;
8#[cfg(all(not(feature = "std"), feature = "alloc"))]
9use alloc::vec::Vec;
10
11/// Plus: an Alt with a zero/empty element.
12///
13/// Laws:
14/// - Left identity: `alt(zero(), a) == a`
15/// - Right identity: `alt(a, zero()) == a`
16/// - Annihilation: `fmap(f, zero()) == zero()`
17pub trait Plus: Alt {
18    fn zero<A>() -> Self::Of<A>;
19}
20
21impl Plus for OptionF {
22    fn zero<A>() -> Option<A> {
23        None
24    }
25}
26
27// No Plus for ResultF — can't produce a Result<A, E> without an E value.
28
29#[cfg(any(feature = "std", feature = "alloc"))]
30impl Plus for VecF {
31    fn zero<A>() -> Vec<A> {
32        Vec::new()
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn option_zero() {
42        assert_eq!(OptionF::zero::<i32>(), None);
43    }
44
45    #[test]
46    fn vec_zero() {
47        assert_eq!(VecF::zero::<i32>(), Vec::<i32>::new());
48    }
49}
50
51#[cfg(test)]
52mod law_tests {
53    use super::*;
54    use crate::alt::Alt;
55    use crate::functor::Functor;
56    use proptest::prelude::*;
57
58    proptest! {
59        // Left identity: alt(zero(), a) == a
60        #[test]
61        fn option_left_identity(a in any::<Option<i32>>()) {
62            let left = OptionF::alt(OptionF::zero(), a);
63            prop_assert_eq!(left, a);
64        }
65
66        // Right identity: alt(a, zero()) == a
67        #[test]
68        fn option_right_identity(a in any::<Option<i32>>()) {
69            let left = OptionF::alt(a, OptionF::zero());
70            prop_assert_eq!(left, a);
71        }
72
73        // Annihilation: fmap(f, zero()) == zero()
74        #[test]
75        fn option_annihilation(_x in any::<i32>()) {
76            let f = |a: i32| a.wrapping_add(1);
77            let left = OptionF::fmap(OptionF::zero::<i32>(), f);
78            let right = OptionF::zero::<i32>();
79            prop_assert_eq!(left, right);
80        }
81
82        #[test]
83        fn vec_left_identity(a in prop::collection::vec(any::<i32>(), 0..10)) {
84            let left = VecF::alt(VecF::zero(), a.clone());
85            prop_assert_eq!(left, a);
86        }
87
88        #[test]
89        fn vec_right_identity(a in prop::collection::vec(any::<i32>(), 0..10)) {
90            let left = VecF::alt(a.clone(), VecF::zero());
91            prop_assert_eq!(left, a);
92        }
93    }
94}