1#[macro_export]
7macro_rules! forward_compose {
8 ( $f:expr, $g:expr ) => {
9 move |x| $g($f(x))
10 };
11 ( $f:expr, $g:expr, $($rest:expr),+ ) => {
12 move |x| forward_compose!($g, $($rest),+ )($f(x))
13 };
14}
15
16#[macro_export]
18macro_rules! compose {
19 ( $f:expr, $g:expr ) => {
20 move |x| $f($g(x))
21 };
22 ( $f:expr, $g:expr, $($rest:expr),+ ) => {
23 move |x| $f(compose!($g, $($rest),+)(x))
24 };
25}
26
27pub fn compose2<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C
29where
30 F: Fn(B) -> C,
31 G: Fn(A) -> B,
32{
33 move |a: A| f(g(a))
34}
35
36pub fn compose3<A, B, C, D, F, G, H>(f: F, g: G, h: H) -> impl Fn(A) -> D
37where
38 F: Fn(C) -> D,
39 G: Fn(B) -> C,
40 H: Fn(A) -> B,
41{
42 move |a: A| f(g(h(a)))
43}
44
45pub fn compose4<A, B, C, D, E, F1, F2, F3, F4>(f: F1, g: F2, h: F3, i: F4) -> impl Fn(A) -> E
46where
47 F1: Fn(D) -> E,
48 F2: Fn(C) -> D,
49 F3: Fn(B) -> C,
50 F4: Fn(A) -> B,
51{
52 move |a: A| f(g(h(i(a))))
53}
54
55pub fn compose2_res<A, B, C, E, F, G>(f: F, g: G) -> impl Fn(A) -> Result<C, E>
60where
61 F: Fn(B) -> Result<C, E>,
62 G: Fn(A) -> Result<B, E>,
63{
64 move |a: A| g(a).and_then(|b| f(b))
65}
66
67pub fn compose3_res<A, B, C, D, E, F1, F2, F3>(f: F1, g: F2, h: F3) -> impl Fn(A) -> Result<D, E>
68where
69 F1: Fn(C) -> Result<D, E>,
70 F2: Fn(B) -> Result<C, E>,
71 F3: Fn(A) -> Result<B, E>,
72{
73 move |a: A| h(a).and_then(|b| g(b)).and_then(|c| f(c))
74}
75
76#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_compose2() {
85 let f = |x: i32| x + 1;
86 let g = |x: i32| x * 2;
87 let h = compose2(f, g);
88 assert_eq!(h(3), 7); }
90
91 #[test]
92 fn test_compose3() {
93 let f = |x: i32| x + 1;
94 let g = |x: i32| x * 2;
95 let h = |x: i32| x - 5;
96 let c = compose3(f, g, h);
97 assert_eq!(c(10), 11); }
99
100 #[test]
101 fn test_compose_res() {
102 let f = |x: i32| if x > 0 { Ok(x + 1) } else { Err("f failed") };
103 let g = |x: i32| {
104 if x % 2 == 0 {
105 Ok(x / 2)
106 } else {
107 Err("g failed")
108 }
109 };
110
111 let h = compose2_res(f, g);
112 assert_eq!(h(4), Ok(3)); assert_eq!(h(3), Err("g failed"));
114 }
115
116 #[test]
117 fn test_macro_compose() {
118 let f = |x: i32| x + 1;
119 let g = |x: i32| x * 2;
120 let h = |x: i32| x - 3;
121
122 let comp = compose!(f, g, h);
123 assert_eq!(comp(5), 5); }
125
126 #[test]
127 #[test]
128 fn test_forward_compose_two() {
129 let f = |x: i32| x + 1;
130 let g = |x: i32| x * 2;
131
132 let comp = forward_compose!(f, g);
133 assert_eq!(comp(5), 12); }
135
136 #[test]
137 fn test_forward_compose_three() {
138 let f = |x: i32| x + 1;
139 let g = |x: i32| x * 2;
140 let h = |x: i32| x - 3;
141
142 let comp = forward_compose!(f, g, h);
143 let result = comp(5);
144 assert_eq!(result, 9); }
146
147 #[test]
148 fn test_compose_identity() {
149 let id = |x: i32| x;
150 let f = |x: i32| x + 42;
151
152 let comp = compose!(f, id);
153 assert_eq!(comp(0), 42);
154 }
155
156 #[test]
157 fn test_forward_compose_identity() {
158 let id = |x: i32| x;
159 let f = |x: i32| x + 42;
160
161 let comp = forward_compose!(id, f);
162 assert_eq!(comp(0), 42);
163 }
164}