1use crate::hkt::{EnvF, HKT, IdentityF, OptionF, ResultF};
2#[cfg(any(feature = "std", feature = "alloc"))]
3use crate::hkt::{NonEmptyVec, NonEmptyVecF, VecF};
4
5pub trait Invariant: HKT {
14 fn invmap<A, B>(fa: Self::Of<A>, f: impl Fn(A) -> B, g: impl Fn(B) -> A) -> Self::Of<B>;
15}
16
17impl Invariant for OptionF {
18 fn invmap<A, B>(fa: Option<A>, f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> Option<B> {
19 fa.map(f)
20 }
21}
22
23impl<E> Invariant for ResultF<E> {
24 fn invmap<A, B>(fa: Result<A, E>, f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> Result<B, E> {
25 fa.map(f)
26 }
27}
28
29#[cfg(any(feature = "std", feature = "alloc"))]
30impl Invariant for VecF {
31 fn invmap<A, B>(fa: Vec<A>, f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> Vec<B> {
32 fa.into_iter().map(f).collect()
33 }
34}
35
36impl Invariant for IdentityF {
37 fn invmap<A, B>(fa: A, f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> B {
38 f(fa)
39 }
40}
41
42#[cfg(any(feature = "std", feature = "alloc"))]
43impl Invariant for NonEmptyVecF {
44 fn invmap<A, B>(fa: NonEmptyVec<A>, f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> NonEmptyVec<B> {
45 NonEmptyVec::new(f(fa.head), fa.tail.into_iter().map(&f).collect())
46 }
47}
48
49impl<E> Invariant for EnvF<E> {
50 fn invmap<A, B>(fa: (E, A), f: impl Fn(A) -> B, _g: impl Fn(B) -> A) -> (E, B) {
51 (fa.0, f(fa.1))
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn option_invmap() {
61 let result = OptionF::invmap(Some(3), |x| x * 2, |x| x / 2);
62 assert_eq!(result, Some(6));
63 }
64
65 #[test]
66 fn option_invmap_none() {
67 let result = OptionF::invmap(None::<i32>, |x| x * 2, |x| x / 2);
68 assert_eq!(result, None);
69 }
70
71 #[test]
72 fn result_invmap() {
73 let result = ResultF::<&str>::invmap(Ok(5), |x| x + 1, |x| x - 1);
74 assert_eq!(result, Ok(6));
75 }
76
77 #[test]
78 fn vec_invmap() {
79 let result = VecF::invmap(vec![1, 2, 3], |x| x * 2, |x| x / 2);
80 assert_eq!(result, vec![2, 4, 6]);
81 }
82
83 #[test]
84 fn identity_invmap() {
85 let result = IdentityF::invmap(42, |x| x + 1, |x| x - 1);
86 assert_eq!(result, 43);
87 }
88
89 #[test]
90 fn nonemptyvec_invmap() {
91 let nev = NonEmptyVec::new(1, vec![2, 3]);
92 let result = NonEmptyVecF::invmap(nev, |x| x * 10, |x| x / 10);
93 assert_eq!(result, NonEmptyVec::new(10, vec![20, 30]));
94 }
95
96 #[test]
97 fn env_invmap() {
98 let result = EnvF::<&str>::invmap(("hello", 42), |x| x + 1, |x| x - 1);
99 assert_eq!(result, ("hello", 43));
100 }
101}
102
103#[cfg(test)]
104mod law_tests {
105 use super::*;
106 use proptest::prelude::*;
107
108 proptest! {
109 #[test]
111 fn option_identity(x in any::<Option<i32>>()) {
112 let result = OptionF::invmap(x, |a| a, |a| a);
113 prop_assert_eq!(result, x);
114 }
115
116 #[test]
118 fn option_composition(x in any::<Option<i16>>()) {
119 let f1 = |a: i16| a.wrapping_add(1);
120 let f2 = |a: i16| a.wrapping_sub(1);
121 let g1 = |a: i16| a.wrapping_mul(2);
122 let g2 = |a: i16| a / 2; let left = OptionF::invmap(x, |a| g1(f1(a)), |a| f2(g2(a)));
125 let right = OptionF::invmap(OptionF::invmap(x, f1, f2), g1, g2);
126 prop_assert_eq!(left, right);
127 }
128
129 #[test]
130 fn vec_identity(x in prop::collection::vec(any::<i32>(), 0..10)) {
131 let result = VecF::invmap(x.clone(), |a| a, |a| a);
132 prop_assert_eq!(result, x);
133 }
134
135 #[test]
136 fn vec_composition(x in prop::collection::vec(any::<i16>(), 0..10)) {
137 let f1 = |a: i16| a.wrapping_add(1);
138 let f2 = |a: i16| a.wrapping_sub(1);
139 let g1 = |a: i16| a.wrapping_mul(2);
140 let g2 = |a: i16| a / 2;
141
142 let left = VecF::invmap(x.clone(), |a| g1(f1(a)), |a| f2(g2(a)));
143 let right = VecF::invmap(VecF::invmap(x, f1, f2), g1, g2);
144 prop_assert_eq!(left, right);
145 }
146 }
147}