1use std::sync::Arc;
2
3pub 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
49macro_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
63curry!(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); }
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); }
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 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); assert_eq!(add_to_10_and_5(7), 22); }
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 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}