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