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