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