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