overture_core/
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// pub fn compose4_res<A, B, C, D, E, F, G, F1, F2, F3, F4>(f: F1, g: F2, h: F3, i: F4) -> impl Fn(A) -> Result<D, E>
77// where
78//     F1: Fn(E) -> Result<F, E>,
79//     F2: Fn(D) -> Result<E, E>,
80//     F3: Fn(C) -> Result<D, E>,
81//     F4: Fn(A) -> Result<C, E>,
82// {
83//     move |a: A| f(a).and_then(|b| g(b)).and_then(|c| h(c)).and_then(|d| i(d))
84// }
85
86// ---------------------------------------------------
87// Tests
88// ---------------------------------------------------
89#[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); // f(g(3)) = (3*2)+1
99    }
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); // f(g(h(10))) = (10-5)*2 + 1 = 11
108    }
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)); // g(4) = 2, f(2) = 3
123        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); // h(5)=2, g(2)=4, f(4)=5
134    }
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); // f(5)=6 → g(6)=12
144    }
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); // f(5)=6 → g(6)=12 → h(12)=9
155    }
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}