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