Skip to main content

karpal_core/
applicative.rs

1use crate::apply::Apply;
2use crate::hkt::OptionF;
3use crate::hkt::ResultF;
4#[cfg(any(feature = "std", feature = "alloc"))]
5use crate::hkt::VecF;
6
7/// Applicative: an Apply that can lift a pure value into the functor.
8///
9/// Laws:
10/// - Identity: `ap(pure(id), v) == v`
11/// - Homomorphism: `ap(pure(f), pure(x)) == pure(f(x))`
12/// - Interchange: `ap(u, pure(y)) == ap(pure(|f| f(y)), u)`
13pub trait Applicative: Apply {
14    fn pure<A>(a: A) -> Self::Of<A>;
15}
16
17impl Applicative for OptionF {
18    fn pure<A>(a: A) -> Option<A> {
19        Some(a)
20    }
21}
22
23impl<E> Applicative for ResultF<E> {
24    fn pure<A>(a: A) -> Result<A, E> {
25        Ok(a)
26    }
27}
28
29#[cfg(any(feature = "std", feature = "alloc"))]
30impl Applicative for VecF {
31    fn pure<A>(a: A) -> Vec<A> {
32        vec![a]
33    }
34}
35
36impl Applicative for crate::hkt::IdentityF {
37    fn pure<A>(a: A) -> A {
38        a
39    }
40}
41
42#[cfg(any(feature = "std", feature = "alloc"))]
43impl Applicative for crate::hkt::NonEmptyVecF {
44    fn pure<A>(a: A) -> crate::hkt::NonEmptyVec<A> {
45        crate::hkt::NonEmptyVec::singleton(a)
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn option_pure() {
55        assert_eq!(OptionF::pure(42), Some(42));
56    }
57
58    #[test]
59    fn result_pure() {
60        assert_eq!(ResultF::<String>::pure(42), Ok(42));
61    }
62
63    #[test]
64    fn vec_pure() {
65        assert_eq!(VecF::pure(42), vec![42]);
66    }
67}
68
69#[cfg(test)]
70mod law_tests {
71    use super::*;
72    use proptest::prelude::*;
73
74    proptest! {
75        // Identity: ap(pure(id), v) == v
76        #[test]
77        fn option_identity(x in any::<Option<i32>>()) {
78            let id_fn: Option<fn(i32) -> i32> = OptionF::pure(|a| a);
79            let result = OptionF::ap(id_fn, x.clone());
80            prop_assert_eq!(result, x);
81        }
82
83        // Homomorphism: ap(pure(f), pure(x)) == pure(f(x))
84        #[test]
85        fn option_homomorphism(x in any::<i32>()) {
86            let f = |a: i32| a.wrapping_add(1);
87            let left = OptionF::ap(OptionF::pure(f as fn(i32) -> i32), OptionF::pure(x));
88            let right = OptionF::pure(f(x));
89            prop_assert_eq!(left, right);
90        }
91
92        // Interchange: ap(u, pure(y)) == ap(pure(|f| f(y)), u)
93        #[test]
94        fn option_interchange(y in any::<i16>()) {
95            let u: Option<fn(i16) -> i16> = Some(|a| a.wrapping_mul(2));
96            let left = OptionF::ap(u, OptionF::pure(y));
97            let right = OptionF::ap(
98                OptionF::pure(move |f: fn(i16) -> i16| f(y)),
99                Some(|a: i16| a.wrapping_mul(2) as i16),
100            );
101            prop_assert_eq!(left, right);
102        }
103
104        #[test]
105        fn vec_identity(x in prop::collection::vec(any::<i32>(), 0..10)) {
106            let id_fn: Vec<fn(i32) -> i32> = VecF::pure(|a| a);
107            let result = VecF::ap(id_fn, x.clone());
108            prop_assert_eq!(result, x);
109        }
110
111        #[test]
112        fn vec_homomorphism(x in any::<i32>()) {
113            let f = |a: i32| a.wrapping_add(1);
114            let left = VecF::ap(VecF::pure(f as fn(i32) -> i32), VecF::pure(x));
115            let right = VecF::pure(f(x));
116            prop_assert_eq!(left, right);
117        }
118    }
119}