rust_overture/
curry.rs

1use std::sync::Arc;
2
3// Curry functions for Rust
4pub fn curry2<A1, A2, R, F>(function: F) -> impl Fn(A1) -> Arc<dyn Fn(A2) -> R + Send + Sync>
5where
6    F: Fn(A1, A2) -> R + Send + Sync + Copy + 'static,
7    A1: Clone + Send + Sync + 'static,
8    A2: Send + Sync + 'static,
9    R: Send + Sync + 'static,
10{
11    move |a1: A1| {
12        let a1_clone = a1.clone();
13        Arc::new(move |a2: A2| function(a1_clone.clone(), a2))
14    }
15}
16
17pub fn curry2_throwing<A1, A2, R, E, F>(function: F) -> impl Fn(A1) -> Arc<dyn Fn(A2) -> Result<R, E> + Send + Sync>
18where
19    F: Fn(A1, A2) -> Result<R, E> + Send + Sync + Copy + 'static,
20    A1: Clone + Send + Sync + 'static,
21    A2: Send + Sync + 'static,
22    R: Send + Sync + 'static,
23    E: Send + Sync + 'static,
24{
25    move |a1: A1| {
26        let a1_clone = a1.clone();
27        Arc::new(move |a2: A2| function(a1_clone.clone(), a2))
28    }
29}
30
31pub fn curry3<A1, A2, A3, R, F>(function: F) -> impl Fn(A1) -> Arc<dyn Fn(A2) -> Arc<dyn Fn(A3) -> R + Send + Sync> + Send + Sync>
32where
33    F: Fn(A1, A2, A3) -> R + Send + Sync + Copy + 'static,
34    A1: Clone + Send + Sync + 'static,
35    A2: Clone + Send + Sync + 'static,
36    A3: Send + Sync + 'static,
37    R: Send + Sync + 'static,
38{
39    move |a1: A1| {
40        let a1_clone = a1.clone();
41        Arc::new(move |a2: A2| {
42            let a1_clone = a1_clone.clone();
43            let a2_clone = a2.clone();
44            Arc::new(move |a3: A3| function(a1_clone.clone(), a2_clone.clone(), a3))
45        })
46    }
47}
48
49// Macro for higher arity functions - using Arc pattern
50macro_rules! curry {
51    ($name:ident, $($arg:ident),+) => {
52        pub fn $name<F, R, $($arg),+>(function: F) -> impl Fn($($arg),+) -> R
53        where
54            F: Fn($($arg),+) -> R + Copy + 'static,
55            $( $arg: Clone + 'static, )+
56            R: 'static,
57        {
58            move |$($arg),+| function($($arg.clone()),+)
59        }
60    };
61}
62
63// Generate curry functions using macro
64curry!(curry4, A1, A2, A3, A4);
65curry!(curry5, A1, A2, A3, A4, A5);
66curry!(curry6, A1, A2, A3, A4, A5, A6);
67curry!(curry7, A1, A2, A3, A4, A5, A6, A7);
68curry!(curry8, A1, A2, A3, A4, A5, A6, A7, A8);
69curry!(curry9, A1, A2, A3, A4, A5, A6, A7, A8, A9);
70curry!(curry10, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_curry2() {
78        let add = |a: i32, b: i32| a + b;
79        let curried = curry2(add);
80        let add2 = curried(2);
81        assert_eq!(add2(3), 5);
82        assert_eq!(add2(7), 9);
83    }
84
85    #[test]
86    fn test_curry2_throwing() {
87        let safe_divide = |a: f64, b: f64| {
88            if b == 0.0 {
89                Err("Division by zero".to_string())
90            } else {
91                Ok(a / b)
92            }
93        };
94        let curried = curry2_throwing(safe_divide);
95        let divide_by_2 = curried(10.0);
96        
97        assert_eq!(divide_by_2(2.0), Ok(5.0));
98        assert_eq!(divide_by_2(0.0), Err("Division by zero".to_string()));
99    }
100
101    #[test]
102    fn test_curry3() {
103        let multiply_add = |a: i32, b: i32, c: i32| a * b + c;
104        let curried = curry3(multiply_add);
105        let multiply_by_2 = curried(2);
106        let multiply_by_2_add = multiply_by_2(3);
107        assert_eq!(multiply_by_2_add(4), 10); // 2*3 + 4 = 10
108    }
109
110    #[test]
111    fn test_curry4_macro() {
112        let complex_calc = |a: i32, b: i32, c: i32, d: i32| (a + b) * (c - d);
113        let result = curry4(complex_calc)(1, 2, 5, 3);
114        assert_eq!(result, 6); // (1+2)*(5-3) = 6
115    }
116
117    #[test]
118    fn test_curry5_macro() {
119        let fn5 = |a: i32, b: i32, c: i32, d: i32, e: i32| a + b + c + d + e;
120        let result = curry5(fn5)(1, 2, 3, 4, 5);
121        assert_eq!(result, 15);
122    }
123
124    #[test]
125    fn test_string_operations() {
126        let concat = |a: String, b: String| format!("{}-{}", a, b);
127        let curried = curry2(concat);
128        let hello_prefix = curried("hello".to_string());
129        let result = hello_prefix("world".to_string());
130        assert_eq!(result, "hello-world");
131    }
132
133    #[test]
134    fn test_partial_application() {
135        let add_three = |a: i32, b: i32, c: i32| a + b + c;
136        let curried = curry3(add_three);
137        
138        // Partial application
139        let add_to_10 = curried(10);
140        let add_to_10_and_5 = add_to_10(5);
141        
142        assert_eq!(add_to_10_and_5(3), 18); // 10 + 5 + 3 = 18
143        assert_eq!(add_to_10_and_5(7), 22); // 10 + 5 + 7 = 22
144    }
145
146    #[test]
147    fn test_different_types() {
148        let create_tuple = |a: i32, b: String, c: bool| (a, b, c);
149        let curried = curry3(create_tuple);
150        let with_number = curried(42);
151        let with_number_and_str = with_number("hello".to_string());
152        let result = with_number_and_str(true);
153        assert_eq!(result, (42, "hello".to_string(), true));
154    }
155
156    #[test]
157    fn test_curry6_macro() {
158        let fn6 = |a: i32, b: i32, c: i32, d: i32, e: i32, f: i32| a + b + c + d + e + f;
159        let result = curry6(fn6)(1, 2, 3, 4, 5, 6);
160        assert_eq!(result, 21);
161    }
162
163    #[test]
164    fn test_curry7_macro() {
165        let fn7 = |a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32| a + b + c + d + e + f + g;
166        let result = curry7(fn7)(1, 2, 3, 4, 5, 6, 7);
167        assert_eq!(result, 28);
168    }
169
170    #[test]
171    fn test_thread_safety() {
172        // Test that our curried functions can be sent between threads
173        let add = |a: i32, b: i32| a + b;
174        let curried = curry2(add);
175        let add5 = curried(5);
176        
177        let handle = std::thread::spawn(move || {
178            add5(3)
179        });
180        
181        assert_eq!(handle.join().unwrap(), 8);
182    }
183}