1use karpal_core::hkt::HKT2;
2
3use crate::arrow::Arrow;
4use crate::arrow_apply::ArrowApply;
5use crate::arrow_choice::ArrowChoice;
6use crate::arrow_loop::ArrowLoop;
7use crate::category::Category;
8use crate::semigroupoid::Semigroupoid;
9
10pub struct FnA;
15
16impl HKT2 for FnA {
17 type P<A, B> = Box<dyn Fn(A) -> B>;
18}
19
20impl Semigroupoid for FnA {
21 fn compose<A: Clone + 'static, B: Clone + 'static, C: Clone + 'static>(
22 f: Box<dyn Fn(B) -> C>,
23 g: Box<dyn Fn(A) -> B>,
24 ) -> Box<dyn Fn(A) -> C> {
25 Box::new(move |a| f(g(a)))
26 }
27}
28
29impl Category for FnA {
30 fn id<A: Clone + 'static>() -> Box<dyn Fn(A) -> A> {
31 Box::new(|a| a)
32 }
33}
34
35impl Arrow for FnA {
36 fn arr<A: Clone + 'static, B: Clone + 'static>(
37 f: impl Fn(A) -> B + 'static,
38 ) -> Box<dyn Fn(A) -> B> {
39 Box::new(f)
40 }
41
42 fn first<A: Clone + 'static, B: Clone + 'static, C: Clone + 'static>(
43 pab: Box<dyn Fn(A) -> B>,
44 ) -> Box<dyn Fn((A, C)) -> (B, C)> {
45 Box::new(move |(a, c)| (pab(a), c))
46 }
47
48 fn second<A: Clone + 'static, B: Clone + 'static, C: Clone + 'static>(
49 pab: Box<dyn Fn(A) -> B>,
50 ) -> Box<dyn Fn((C, A)) -> (C, B)> {
51 Box::new(move |(c, a)| (c, pab(a)))
52 }
53}
54
55impl ArrowChoice for FnA {
56 fn left<A: Clone + 'static, B: Clone + 'static, C: Clone + 'static>(
57 pab: Box<dyn Fn(A) -> B>,
58 ) -> Box<dyn Fn(Result<A, C>) -> Result<B, C>> {
59 Box::new(move |r| match r {
60 Ok(a) => Ok(pab(a)),
61 Err(c) => Err(c),
62 })
63 }
64
65 fn right<A: Clone + 'static, B: Clone + 'static, C: Clone + 'static>(
66 pab: Box<dyn Fn(A) -> B>,
67 ) -> Box<dyn Fn(Result<C, A>) -> Result<C, B>> {
68 Box::new(move |r| match r {
69 Ok(c) => Ok(c),
70 Err(a) => Err(pab(a)),
71 })
72 }
73}
74
75impl ArrowApply for FnA {
76 fn app<A: Clone + 'static, B: Clone + 'static>() -> Box<dyn Fn((Box<dyn Fn(A) -> B>, A)) -> B> {
77 Box::new(|(f, a)| f(a))
78 }
79}
80
81impl ArrowLoop for FnA {
82 fn loop_arrow<A: Clone + 'static, B: Clone + 'static, D: Default + Clone + 'static>(
83 f: Box<dyn Fn((A, D)) -> (B, D)>,
84 ) -> Box<dyn Fn(A) -> B> {
85 Box::new(move |a| {
86 let (b, _d) = f((a, D::default()));
87 b
88 })
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn fna_compose() {
98 let f: Box<dyn Fn(i32) -> i32> = Box::new(|x| x + 1);
99 let g: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
100 let fg = FnA::compose(f, g);
101 assert_eq!(fg(3), 7); }
103
104 #[test]
105 fn fna_id() {
106 let id = FnA::id::<i32>();
107 assert_eq!(id(42), 42);
108 }
109
110 #[test]
111 fn fna_arr() {
112 let f = FnA::arr(|x: i32| x.to_string());
113 assert_eq!(f(42), "42");
114 }
115
116 #[test]
117 fn fna_first() {
118 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
119 let f = FnA::first::<i32, i32, &str>(double);
120 assert_eq!(f((5, "hi")), (10, "hi"));
121 }
122
123 #[test]
124 fn fna_second() {
125 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
126 let f = FnA::second::<i32, i32, &str>(double);
127 assert_eq!(f(("hi", 5)), ("hi", 10));
128 }
129
130 #[test]
131 fn fna_split() {
132 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
133 let negate: Box<dyn Fn(i32) -> i32> = Box::new(|x| -x);
134 let f = FnA::split(double, negate);
135 assert_eq!(f((3, 4)), (6, -4));
136 }
137
138 #[test]
139 fn fna_fanout() {
140 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
141 let negate: Box<dyn Fn(i32) -> i32> = Box::new(|x| -x);
142 let f = FnA::fanout(double, negate);
143 assert_eq!(f(5), (10, -5));
144 }
145
146 #[test]
147 fn fna_left() {
148 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
149 let f = FnA::left::<i32, i32, &str>(double);
150 assert_eq!(f(Ok(5)), Ok(10));
151 assert_eq!(f(Err("nope")), Err("nope"));
152 }
153
154 #[test]
155 fn fna_right() {
156 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
157 let f = FnA::right::<i32, i32, &str>(double);
158 assert_eq!(f(Err(5)), Err(10));
159 assert_eq!(f(Ok("yep")), Ok("yep"));
160 }
161
162 #[test]
163 fn fna_splat() {
164 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
165 let len: Box<dyn Fn(String) -> usize> = Box::new(|s| s.len());
166 let f = FnA::splat(double, len);
167 assert_eq!(f(Ok(5)), Ok(10));
168 assert_eq!(f(Err("hello".to_string())), Err(5));
169 }
170
171 #[test]
172 fn fna_fanin() {
173 let double: Box<dyn Fn(i32) -> String> = Box::new(|x| format!("int:{}", x));
174 let show: Box<dyn Fn(bool) -> String> = Box::new(|b| format!("bool:{}", b));
175 let f = FnA::fanin(double, show);
176 assert_eq!(f(Ok(42)), "int:42");
177 assert_eq!(f(Err(true)), "bool:true");
178 }
179
180 #[test]
181 fn fna_app() {
182 let app = FnA::app::<i32, i32>();
183 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
184 assert_eq!(app((double, 5)), 10);
185 }
186
187 #[test]
188 fn fna_loop_arrow() {
189 let f = FnA::loop_arrow::<i32, i32, i32>(Box::new(|(a, d)| (a + d, d)));
191 assert_eq!(f(5), 5); }
193}
194
195#[cfg(test)]
196mod law_tests {
197 use super::*;
198 use proptest::prelude::*;
199
200 proptest! {
201 #[test]
203 fn associativity(x in any::<i16>()) {
204 let f = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_add(1)) };
205 let g = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_mul(2)) };
206 let h = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_sub(3)) };
207
208 let left = FnA::compose(f(), FnA::compose(g(), h()));
209 let right = FnA::compose(FnA::compose(f(), g()), h());
210 prop_assert_eq!(left(x), right(x));
211 }
212
213 #[test]
215 fn left_identity(x in any::<i16>()) {
216 let f = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_mul(2)) };
217 let left = FnA::compose(FnA::id(), f());
218 let right = f();
219 prop_assert_eq!(left(x), right(x));
220 }
221
222 #[test]
224 fn right_identity(x in any::<i16>()) {
225 let f = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_mul(2)) };
226 let left = FnA::compose(f(), FnA::id());
227 let right = f();
228 prop_assert_eq!(left(x), right(x));
229 }
230
231 #[test]
233 fn arr_id(x in any::<i16>()) {
234 let left = FnA::arr(|a: i16| a);
235 let right = FnA::id::<i16>();
236 prop_assert_eq!(left(x), right(x));
237 }
238
239 #[test]
241 fn arr_composition(x in any::<i16>()) {
242 let f = |a: i16| a.wrapping_add(1);
243 let g = |a: i16| a.wrapping_mul(2);
244
245 let left = FnA::arr(move |a: i16| g(f(a)));
246 let right = FnA::compose(FnA::arr(g), FnA::arr(f));
247 prop_assert_eq!(left(x), right(x));
248 }
249
250 #[test]
252 fn first_arr(x in any::<i16>(), c in any::<i16>()) {
253 let f = |a: i16| a.wrapping_add(1);
254 let left = FnA::first::<i16, i16, i16>(FnA::arr(f));
255 let right = FnA::arr(move |(a, c): (i16, i16)| (f(a), c));
256 prop_assert_eq!(left((x, c)), right((x, c)));
257 }
258
259 #[test]
261 fn first_compose(x in any::<i16>(), c in any::<i16>()) {
262 let f = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_add(1)) };
263 let g = || -> Box<dyn Fn(i16) -> i16> { Box::new(|a| a.wrapping_mul(2)) };
264
265 let left = FnA::first::<i16, i16, i16>(FnA::compose(f(), g()));
266 let right = FnA::compose(
267 FnA::first::<i16, i16, i16>(f()),
268 FnA::first::<i16, i16, i16>(g()),
269 );
270 prop_assert_eq!(left((x, c)), right((x, c)));
271 }
272
273 #[test]
275 fn left_arr(x in any::<Result<i16, i16>>()) {
276 let f = |a: i16| a.wrapping_add(1);
277 let left = FnA::left::<i16, i16, i16>(FnA::arr(f));
278 let right = FnA::arr(move |r: Result<i16, i16>| r.map(f));
279 prop_assert_eq!(left(x), right(x));
280 }
281 }
282}