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