1use crate::apply::Apply;
2use crate::hkt::OptionF;
3use crate::hkt::ResultF;
4#[cfg(any(feature = "std", feature = "alloc"))]
5use crate::hkt::VecF;
6
7pub trait Chain: Apply {
12 fn chain<A, B>(fa: Self::Of<A>, f: impl Fn(A) -> Self::Of<B>) -> Self::Of<B>;
13}
14
15impl Chain for OptionF {
16 fn chain<A, B>(fa: Option<A>, f: impl Fn(A) -> Option<B>) -> Option<B> {
17 fa.and_then(f)
18 }
19}
20
21impl<E> Chain for ResultF<E> {
22 fn chain<A, B>(fa: Result<A, E>, f: impl Fn(A) -> Result<B, E>) -> Result<B, E> {
23 fa.and_then(f)
24 }
25}
26
27#[cfg(any(feature = "std", feature = "alloc"))]
28impl Chain for VecF {
29 fn chain<A, B>(fa: Vec<A>, f: impl Fn(A) -> Vec<B>) -> Vec<B> {
30 fa.into_iter().flat_map(f).collect()
31 }
32}
33
34impl Chain for crate::hkt::IdentityF {
35 fn chain<A, B>(fa: A, f: impl Fn(A) -> B) -> B {
36 f(fa)
37 }
38}
39
40#[cfg(any(feature = "std", feature = "alloc"))]
41impl Chain for crate::hkt::NonEmptyVecF {
42 fn chain<A, B>(
43 fa: crate::hkt::NonEmptyVec<A>,
44 f: impl Fn(A) -> crate::hkt::NonEmptyVec<B>,
45 ) -> crate::hkt::NonEmptyVec<B> {
46 let first = f(fa.head);
47 let mut tail = first.tail;
48 for a in fa.tail {
49 let result = f(a);
50 tail.push(result.head);
51 tail.extend(result.tail);
52 }
53 crate::hkt::NonEmptyVec::new(first.head, tail)
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn option_chain_some() {
63 let result = OptionF::chain(Some(3), |x| if x > 0 { Some(x * 2) } else { None });
64 assert_eq!(result, Some(6));
65 }
66
67 #[test]
68 fn option_chain_none() {
69 let result = OptionF::chain(None::<i32>, |x| Some(x * 2));
70 assert_eq!(result, None);
71 }
72
73 #[test]
74 fn result_chain_ok() {
75 let result = ResultF::<&str>::chain(Ok(3), |x| Ok(x + 1));
76 assert_eq!(result, Ok(4));
77 }
78
79 #[test]
80 fn result_chain_err() {
81 let result = ResultF::<&str>::chain(Err("bad"), |x: i32| Ok(x + 1));
82 assert_eq!(result, Err("bad"));
83 }
84
85 #[test]
86 fn vec_chain() {
87 let result = VecF::chain(vec![1, 2, 3], |x| vec![x, x * 10]);
88 assert_eq!(result, vec![1, 10, 2, 20, 3, 30]);
89 }
90}
91
92#[cfg(test)]
93mod law_tests {
94 use super::*;
95 use proptest::prelude::*;
96
97 proptest! {
98 #[test]
100 fn option_associativity(x in any::<i16>()) {
101 let m = Some(x);
102 let f = |a: i16| Some(a.wrapping_add(1));
103 let g = |a: i16| Some(a.wrapping_mul(2));
104
105 let left = OptionF::chain(OptionF::chain(m, f), g);
106 let right = OptionF::chain(m, |a| OptionF::chain(f(a), g));
107 prop_assert_eq!(left, right);
108 }
109
110 #[test]
111 fn vec_associativity(x in prop::collection::vec(any::<i8>(), 0..5)) {
112 let f = |a: i8| vec![a, a.wrapping_add(1)];
113 let g = |a: i8| vec![a.wrapping_mul(2)];
114
115 let left = VecF::chain(VecF::chain(x.clone(), f), g);
116 let right = VecF::chain(x, |a| VecF::chain(f(a), g));
117 prop_assert_eq!(left, right);
118 }
119 }
120}