rust_overture/
compose.rs

1// =======================
2// Forward composition (>>> in Swift)
3// forward_compose!(f, g, h)(x) == h(g(f(x)))
4// =======================
5// Forward composition: h(g(f(x)))
6#[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// backword composition
17#[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
27// Function composition in Rust (normal functions)
28pub 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
55// ---------------------------------------------------
56// Throwing versions (Swift `throws` → Rust `Result`)
57// ---------------------------------------------------
58
59pub 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// ---------------------------------------------------
77// Tests
78// ---------------------------------------------------
79#[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); // f(g(3)) = (3*2)+1
89    }
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); // f(g(h(10))) = (10-5)*2 + 1 = 11
98    }
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)); // g(4) = 2, f(2) = 3
113        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); // h(5)=2, g(2)=4, f(4)=5
124    }
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); // f(5)=6 → g(6)=12
134    }
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); // f(5)=6 → g(6)=12 → h(12)=9
145    }
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}