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