1use karpal_core::hkt::{HKT, IdentityF, OptionF, ResultF};
5
6pub trait FunctorSt: HKT {
13 fn fmap_st<A: 'static, B: 'static>(
14 fa: Self::Of<A>,
15 f: impl Fn(A) -> B + 'static,
16 ) -> Self::Of<B>;
17}
18
19pub trait ApplicativeSt: FunctorSt {
26 fn pure_st<A: 'static>(a: A) -> Self::Of<A>;
27}
28
29pub trait ChainSt: FunctorSt {
31 fn chain_st<A: 'static, B: 'static>(
32 fa: Self::Of<A>,
33 f: impl Fn(A) -> Self::Of<B> + 'static,
34 ) -> Self::Of<B>;
35}
36
37impl FunctorSt for OptionF {
40 fn fmap_st<A: 'static, B: 'static>(fa: Option<A>, f: impl Fn(A) -> B + 'static) -> Option<B> {
41 fa.map(f)
42 }
43}
44
45impl ApplicativeSt for OptionF {
46 fn pure_st<A: 'static>(a: A) -> Option<A> {
47 Some(a)
48 }
49}
50
51impl ChainSt for OptionF {
52 fn chain_st<A: 'static, B: 'static>(
53 fa: Option<A>,
54 f: impl Fn(A) -> Option<B> + 'static,
55 ) -> Option<B> {
56 fa.and_then(f)
57 }
58}
59
60impl<E: 'static> FunctorSt for ResultF<E> {
61 fn fmap_st<A: 'static, B: 'static>(
62 fa: Result<A, E>,
63 f: impl Fn(A) -> B + 'static,
64 ) -> Result<B, E> {
65 fa.map(f)
66 }
67}
68
69impl<E: 'static> ApplicativeSt for ResultF<E> {
70 fn pure_st<A: 'static>(a: A) -> Result<A, E> {
71 Ok(a)
72 }
73}
74
75impl<E: 'static> ChainSt for ResultF<E> {
76 fn chain_st<A: 'static, B: 'static>(
77 fa: Result<A, E>,
78 f: impl Fn(A) -> Result<B, E> + 'static,
79 ) -> Result<B, E> {
80 fa.and_then(f)
81 }
82}
83
84impl FunctorSt for IdentityF {
85 fn fmap_st<A: 'static, B: 'static>(fa: A, f: impl Fn(A) -> B + 'static) -> B {
86 f(fa)
87 }
88}
89
90impl ApplicativeSt for IdentityF {
91 fn pure_st<A: 'static>(a: A) -> A {
92 a
93 }
94}
95
96impl ChainSt for IdentityF {
97 fn chain_st<A: 'static, B: 'static>(fa: A, f: impl Fn(A) -> B + 'static) -> B {
98 f(fa)
99 }
100}
101
102#[cfg(any(feature = "std", feature = "alloc"))]
103impl FunctorSt for karpal_core::hkt::VecF {
104 fn fmap_st<A: 'static, B: 'static>(fa: Vec<A>, f: impl Fn(A) -> B + 'static) -> Vec<B> {
105 fa.into_iter().map(f).collect()
106 }
107}
108
109#[cfg(any(feature = "std", feature = "alloc"))]
110impl ApplicativeSt for karpal_core::hkt::VecF {
111 fn pure_st<A: 'static>(a: A) -> Vec<A> {
112 vec![a]
113 }
114}
115
116#[cfg(any(feature = "std", feature = "alloc"))]
117impl ChainSt for karpal_core::hkt::VecF {
118 fn chain_st<A: 'static, B: 'static>(fa: Vec<A>, f: impl Fn(A) -> Vec<B> + 'static) -> Vec<B> {
119 fa.into_iter().flat_map(f).collect()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn option_functor_st() {
129 assert_eq!(OptionF::fmap_st(Some(3), |x| x + 1), Some(4));
130 assert_eq!(OptionF::fmap_st(None::<i32>, |x| x + 1), None);
131 }
132
133 #[test]
134 fn option_applicative_st() {
135 assert_eq!(OptionF::pure_st(42), Some(42));
136 }
137
138 #[test]
139 fn option_chain_st() {
140 assert_eq!(OptionF::chain_st(Some(3), |x| Some(x * 2)), Some(6));
141 assert_eq!(OptionF::chain_st(None::<i32>, |x| Some(x * 2)), None);
142 }
143
144 #[test]
145 fn result_functor_st() {
146 assert_eq!(ResultF::<&str>::fmap_st(Ok(3), |x| x + 1), Ok(4));
147 }
148
149 #[test]
150 fn identity_chain_st() {
151 assert_eq!(IdentityF::chain_st(5, |x| x + 1), 6);
152 }
153}
154
155#[cfg(test)]
156mod law_tests {
157 use super::*;
158 use proptest::prelude::*;
159
160 proptest! {
161 #[test]
163 fn option_functor_identity(x in any::<Option<i32>>()) {
164 prop_assert_eq!(OptionF::fmap_st(x.clone(), |a| a), x);
165 }
166
167 #[test]
169 fn option_functor_composition(x in any::<Option<i16>>()) {
170 let f = |a: i16| a.wrapping_add(1);
171 let g = |a: i16| a.wrapping_mul(2);
172 let left = OptionF::fmap_st(x.clone(), move |a| g(f(a)));
173 let right = OptionF::fmap_st(OptionF::fmap_st(x, f), g);
174 prop_assert_eq!(left, right);
175 }
176
177 #[test]
179 fn option_chain_associativity(x in any::<Option<i16>>()) {
180 let f = |a: i16| Some(a.wrapping_add(1));
181 let g = |a: i16| Some(a.wrapping_mul(2));
182 let left = OptionF::chain_st(OptionF::chain_st(x.clone(), f), g);
183 let right = OptionF::chain_st(x, move |a| OptionF::chain_st(f(a), g));
184 prop_assert_eq!(left, right);
185 }
186
187 #[test]
189 fn option_monad_left_identity(a in any::<i16>()) {
190 let f = |x: i16| Some(x.wrapping_add(1));
191 let left = OptionF::chain_st(OptionF::pure_st(a), f);
192 let right = f(a);
193 prop_assert_eq!(left, right);
194 }
195
196 #[test]
198 fn option_monad_right_identity(x in any::<Option<i32>>()) {
199 let left = OptionF::chain_st(x.clone(), |a| OptionF::pure_st(a));
200 prop_assert_eq!(left, x);
201 }
202 }
203}