1use core::marker::PhantomData;
5
6use crate::functor::Functor;
7use crate::hkt::HKT;
8
9pub struct ComposeF<F, G>(PhantomData<(F, G)>);
14
15impl<F: HKT, G: HKT> HKT for ComposeF<F, G> {
16 type Of<T> = F::Of<G::Of<T>>;
17}
18
19impl<F: Functor, G: Functor> Functor for ComposeF<F, G> {
21 fn fmap<A, B>(fga: F::Of<G::Of<A>>, f: impl Fn(A) -> B) -> F::Of<G::Of<B>> {
22 F::fmap(fga, |ga| G::fmap(ga, &f))
23 }
24}
25
26#[cfg(test)]
32mod tests {
33 use super::*;
34 #[cfg(any(feature = "std", feature = "alloc"))]
35 use crate::hkt::VecF;
36 use crate::hkt::{IdentityF, OptionF};
37
38 #[test]
39 fn compose_option_option_fmap() {
40 let val: Option<Option<i32>> = Some(Some(42));
42 let result = ComposeF::<OptionF, OptionF>::fmap(val, |x| x + 1);
43 assert_eq!(result, Some(Some(43)));
44 }
45
46 #[test]
47 fn compose_option_option_fmap_outer_none() {
48 let val: Option<Option<i32>> = None;
49 let result = ComposeF::<OptionF, OptionF>::fmap(val, |x| x + 1);
50 assert_eq!(result, None);
51 }
52
53 #[test]
54 fn compose_option_option_fmap_inner_none() {
55 let val: Option<Option<i32>> = Some(None);
56 let result = ComposeF::<OptionF, OptionF>::fmap(val, |x| x + 1);
57 assert_eq!(result, Some(None));
58 }
59
60 #[test]
61 fn compose_identity_option() {
62 let val: Option<i32> = Some(10);
64 let result = ComposeF::<IdentityF, OptionF>::fmap(val, |x| x * 2);
65 assert_eq!(result, Some(20));
66 }
67
68 #[test]
69 fn compose_option_identity() {
70 let val: Option<i32> = Some(10);
72 let result = ComposeF::<OptionF, IdentityF>::fmap(val, |x| x * 2);
73 assert_eq!(result, Some(20));
74 }
75
76 #[cfg(any(feature = "std", feature = "alloc"))]
77 #[test]
78 fn compose_vec_option_fmap() {
79 let val: Vec<Option<i32>> = vec![Some(1), None, Some(3)];
81 let result = ComposeF::<VecF, OptionF>::fmap(val, |x| x * 10);
82 assert_eq!(result, vec![Some(10), None, Some(30)]);
83 }
84
85 #[cfg(any(feature = "std", feature = "alloc"))]
86 #[test]
87 fn compose_option_vec_fmap() {
88 let val: Option<Vec<i32>> = Some(vec![1, 2, 3]);
90 let result = ComposeF::<OptionF, VecF>::fmap(val, |x| x + 1);
91 assert_eq!(result, Some(vec![2, 3, 4]));
92 }
93}
94
95#[cfg(test)]
96mod law_tests {
97 use super::*;
98 use crate::hkt::OptionF;
99 use proptest::prelude::*;
100
101 proptest! {
102 #[test]
104 fn compose_option_option_identity(x in any::<Option<Option<i32>>>()) {
105 let result = ComposeF::<OptionF, OptionF>::fmap(x.clone(), |a| a);
106 prop_assert_eq!(result, x);
107 }
108
109 #[test]
111 fn compose_option_option_composition(x in any::<Option<Option<i32>>>()) {
112 let f = |a: i32| a.wrapping_add(1);
113 let g = |a: i32| a.wrapping_mul(2);
114 let left = ComposeF::<OptionF, OptionF>::fmap(x.clone(), |a| g(f(a)));
115 let right = ComposeF::<OptionF, OptionF>::fmap(
116 ComposeF::<OptionF, OptionF>::fmap(x, f),
117 g,
118 );
119 prop_assert_eq!(left, right);
120 }
121 }
122}