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)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn test_compose2() {
95 let f = |x: i32| x + 1;
96 let g = |x: i32| x * 2;
97 let h = compose2(f, g);
98 assert_eq!(h(3), 7); }
100
101 #[test]
102 fn test_compose3() {
103 let f = |x: i32| x + 1;
104 let g = |x: i32| x * 2;
105 let h = |x: i32| x - 5;
106 let c = compose3(f, g, h);
107 assert_eq!(c(10), 11); }
109
110 #[test]
111 fn test_compose_res() {
112 let f = |x: i32| if x > 0 { Ok(x + 1) } else { Err("f failed") };
113 let g = |x: i32| {
114 if x % 2 == 0 {
115 Ok(x / 2)
116 } else {
117 Err("g failed")
118 }
119 };
120
121 let h = compose2_res(f, g);
122 assert_eq!(h(4), Ok(3)); assert_eq!(h(3), Err("g failed"));
124 }
125
126 #[test]
127 fn test_macro_compose() {
128 let f = |x: i32| x + 1;
129 let g = |x: i32| x * 2;
130 let h = |x: i32| x - 3;
131
132 let comp = compose!(f, g, h);
133 assert_eq!(comp(5), 5); }
135
136 #[test]
137 #[test]
138 fn test_forward_compose_two() {
139 let f = |x: i32| x + 1;
140 let g = |x: i32| x * 2;
141
142 let comp = forward_compose!(f, g);
143 assert_eq!(comp(5), 12); }
145
146 #[test]
147 fn test_forward_compose_three() {
148 let f = |x: i32| x + 1;
149 let g = |x: i32| x * 2;
150 let h = |x: i32| x - 3;
151
152 let comp = forward_compose!(f, g, h);
153 let result = comp(5);
154 assert_eq!(result, 9); }
156
157 #[test]
158 fn test_compose_identity() {
159 let id = |x: i32| x;
160 let f = |x: i32| x + 42;
161
162 let comp = compose!(f, id);
163 assert_eq!(comp(0), 42);
164 }
165
166 #[test]
167 fn test_forward_compose_identity() {
168 let id = |x: i32| x;
169 let f = |x: i32| x + 42;
170
171 let comp = forward_compose!(id, f);
172 assert_eq!(comp(0), 42);
173 }
174}