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