1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use crate::choice::Choice;
5use crate::profunctor::{HKT2, Profunctor};
6use crate::strong::Strong;
7use crate::traversing::Traversing;
8
9pub struct FnP;
13
14impl HKT2 for FnP {
15 type P<A, B> = Box<dyn Fn(A) -> B>;
16}
17
18impl Profunctor for FnP {
19 fn dimap<A: 'static, B: 'static, C, D>(
20 f: impl Fn(C) -> A + 'static,
21 g: impl Fn(B) -> D + 'static,
22 pab: Box<dyn Fn(A) -> B>,
23 ) -> Box<dyn Fn(C) -> D> {
24 Box::new(move |c| g(pab(f(c))))
25 }
26}
27
28impl Strong for FnP {
29 fn first<A, B, C>(pab: Box<dyn Fn(A) -> B>) -> Box<dyn Fn((A, C)) -> (B, C)>
30 where
31 A: 'static,
32 B: 'static,
33 C: 'static,
34 {
35 Box::new(move |(a, c)| (pab(a), c))
36 }
37
38 fn second<A, B, C>(pab: Box<dyn Fn(A) -> B>) -> Box<dyn Fn((C, A)) -> (C, B)>
39 where
40 A: 'static,
41 B: 'static,
42 C: 'static,
43 {
44 Box::new(move |(c, a)| (c, pab(a)))
45 }
46}
47
48impl Traversing for FnP {
49 fn wander<S, T, A, B>(
50 _get_all: impl Fn(&S) -> Vec<A> + 'static,
51 modify_all: impl Fn(S, &dyn Fn(A) -> B) -> T + 'static,
52 pab: Box<dyn Fn(A) -> B>,
53 ) -> Box<dyn Fn(S) -> T>
54 where
55 S: 'static,
56 T: 'static,
57 A: 'static,
58 B: 'static,
59 {
60 Box::new(move |s| modify_all(s, &*pab))
61 }
62}
63
64impl Choice for FnP {
65 fn left<A, B, C>(pab: Box<dyn Fn(A) -> B>) -> Box<dyn Fn(Result<A, C>) -> Result<B, C>>
66 where
67 A: 'static,
68 B: 'static,
69 C: 'static,
70 {
71 Box::new(move |r| match r {
72 Ok(a) => Ok(pab(a)),
73 Err(c) => Err(c),
74 })
75 }
76
77 fn right<A, B, C>(pab: Box<dyn Fn(A) -> B>) -> Box<dyn Fn(Result<C, A>) -> Result<C, B>>
78 where
79 A: 'static,
80 B: 'static,
81 C: 'static,
82 {
83 Box::new(move |r| match r {
84 Ok(c) => Ok(c),
85 Err(a) => Err(pab(a)),
86 })
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn fnp_dimap() {
96 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
97 let f = FnP::dimap(|s: &str| s.len() as i32, |n: i32| n.to_string(), double);
98 assert_eq!(f("hello"), "10");
99 }
100
101 #[test]
102 fn fnp_first() {
103 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
104 let f = FnP::first::<i32, i32, &str>(double);
105 assert_eq!(f((5, "hi")), (10, "hi"));
106 }
107
108 #[test]
109 fn fnp_second() {
110 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
111 let f = FnP::second::<i32, i32, &str>(double);
112 assert_eq!(f(("hi", 5)), ("hi", 10));
113 }
114
115 #[test]
116 fn fnp_left() {
117 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
118 let f = FnP::left::<i32, i32, &str>(double);
119 assert_eq!(f(Ok(5)), Ok(10));
120 assert_eq!(f(Err("nope")), Err("nope"));
121 }
122
123 #[test]
124 fn fnp_right() {
125 let double: Box<dyn Fn(i32) -> i32> = Box::new(|x| x * 2);
126 let f = FnP::right::<i32, i32, &str>(double);
127 assert_eq!(f(Err(5)), Err(10));
128 assert_eq!(f(Ok("yep")), Ok("yep"));
129 }
130}
131
132#[cfg(test)]
133mod law_tests {
134 use super::*;
135 use proptest::prelude::*;
136
137 proptest! {
138 #[test]
139 fn profunctor_identity(x in any::<i32>()) {
140 let id_fn: Box<dyn Fn(i32) -> i32> = Box::new(|a| a);
141 let dimapped = FnP::dimap(|a: i32| a, |b: i32| b, id_fn);
142 prop_assert_eq!(dimapped(x), x);
143 }
144
145 #[test]
146 fn profunctor_composition(x in any::<i32>()) {
147 let base: Box<dyn Fn(i32) -> i32> = Box::new(|a| a);
148
149 let f = |a: i32| a.wrapping_add(1);
150 let g = |a: i32| a.wrapping_mul(2);
151 let h = |a: i32| a.wrapping_add(3);
152 let i_fn = |a: i32| a.wrapping_mul(4);
153
154 let left = FnP::dimap(
156 move |a: i32| f(g(a)),
157 move |b: i32| h(i_fn(b)),
158 base,
159 );
160
161 let base2: Box<dyn Fn(i32) -> i32> = Box::new(|a| a);
162 let inner = FnP::dimap(f, i_fn, base2);
164 let right = FnP::dimap(g, h, inner);
165
166 prop_assert_eq!(left(x), right(x));
167 }
168 }
169}