1use crate::hkt::{EnvF, HKT, IdentityF, OptionF, ResultF};
2#[cfg(any(feature = "std", feature = "alloc"))]
3use crate::hkt::{NonEmptyVec, NonEmptyVecF};
4
5pub trait Functor: HKT {
7 fn fmap<A, B>(fa: Self::Of<A>, f: impl Fn(A) -> B) -> Self::Of<B>;
8}
9
10impl Functor for OptionF {
11 fn fmap<A, B>(fa: Option<A>, f: impl Fn(A) -> B) -> Option<B> {
12 fa.map(f)
13 }
14}
15
16impl<E> Functor for ResultF<E> {
17 fn fmap<A, B>(fa: Result<A, E>, f: impl Fn(A) -> B) -> Result<B, E> {
18 fa.map(f)
19 }
20}
21
22#[cfg(any(feature = "std", feature = "alloc"))]
23impl Functor for crate::hkt::VecF {
24 fn fmap<A, B>(fa: Vec<A>, f: impl Fn(A) -> B) -> Vec<B> {
25 fa.into_iter().map(f).collect()
26 }
27}
28
29impl Functor for IdentityF {
30 fn fmap<A, B>(fa: A, f: impl Fn(A) -> B) -> B {
31 f(fa)
32 }
33}
34
35#[cfg(any(feature = "std", feature = "alloc"))]
36impl Functor for NonEmptyVecF {
37 fn fmap<A, B>(fa: NonEmptyVec<A>, f: impl Fn(A) -> B) -> NonEmptyVec<B> {
38 NonEmptyVec::new(f(fa.head), fa.tail.into_iter().map(&f).collect())
39 }
40}
41
42impl<E> Functor for EnvF<E> {
43 fn fmap<A, B>(fa: (E, A), f: impl Fn(A) -> B) -> (E, B) {
44 (fa.0, f(fa.1))
45 }
46}
47
48#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn option_fmap_some() {
58 let result = OptionF::fmap(Some(2), |x| x * 3);
59 assert_eq!(result, Some(6));
60 }
61
62 #[test]
63 fn option_fmap_none() {
64 let result = OptionF::fmap(None::<i32>, |x| x * 3);
65 assert_eq!(result, None);
66 }
67
68 #[test]
69 fn result_fmap_ok() {
70 let result = ResultF::<String>::fmap(Ok(5), |x| x + 1);
71 assert_eq!(result, Ok(6));
72 }
73
74 #[test]
75 fn result_fmap_err() {
76 let result = ResultF::<String>::fmap(Err("bad".to_string()), |x: i32| x + 1);
77 assert_eq!(result, Err("bad".to_string()));
78 }
79
80 #[test]
81 fn vec_fmap() {
82 let result = crate::hkt::VecF::fmap(vec![1, 2, 3], |x| x * 2);
83 assert_eq!(result, vec![2, 4, 6]);
84 }
85}
86
87#[cfg(test)]
88mod law_tests {
89 use super::*;
90 use proptest::prelude::*;
91
92 proptest! {
96 #[test]
97 fn option_identity(x in any::<Option<i32>>()) {
98 let result = OptionF::fmap(x.clone(), |a| a);
99 prop_assert_eq!(result, x);
100 }
101
102 #[test]
103 fn option_composition(x in any::<Option<i32>>()) {
104 let f = |a: i32| a.wrapping_add(1);
105 let g = |a: i32| a.wrapping_mul(2);
106 let left = OptionF::fmap(x.clone(), |a| g(f(a)));
107 let right = OptionF::fmap(OptionF::fmap(x, f), g);
108 prop_assert_eq!(left, right);
109 }
110
111 #[test]
112 fn result_identity(x in any::<Result<i32, u8>>()) {
113 let result = ResultF::<u8>::fmap(x.clone(), |a| a);
114 prop_assert_eq!(result, x);
115 }
116
117 #[test]
118 fn result_composition(x in any::<Result<i32, u8>>()) {
119 let f = |a: i32| a.wrapping_add(1);
120 let g = |a: i32| a.wrapping_mul(2);
121 let left = ResultF::<u8>::fmap(x.clone(), |a| g(f(a)));
122 let right = ResultF::<u8>::fmap(ResultF::<u8>::fmap(x, f), g);
123 prop_assert_eq!(left, right);
124 }
125
126 #[test]
127 fn vec_identity(x in prop::collection::vec(any::<i32>(), 0..20)) {
128 let result = crate::hkt::VecF::fmap(x.clone(), |a| a);
129 prop_assert_eq!(result, x);
130 }
131
132 #[test]
133 fn vec_composition(x in prop::collection::vec(any::<i32>(), 0..20)) {
134 let f = |a: i32| a.wrapping_add(1);
135 let g = |a: i32| a.wrapping_mul(2);
136 let left = crate::hkt::VecF::fmap(x.clone(), |a| g(f(a)));
137 let right = crate::hkt::VecF::fmap(crate::hkt::VecF::fmap(x, f), g);
138 prop_assert_eq!(left, right);
139 }
140 }
141}