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