074: Currying and Partial Application
Difficulty: Intermediate Category: Functional Patterns Concept: Currying (multi-arg โ chain of single-arg) and partial application Key Insight: OCaml functions are curried by default; Rust requires closures to achieve the same effect.// 074: Currying and Partial Application
// Approach 1: Closures for partial application
fn add(x: i32, y: i32) -> i32 { x + y }
fn multiply(x: i32, y: i32) -> i32 { x * y }
fn make_adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
fn make_multiplier(x: i32) -> impl Fn(i32) -> i32 {
move |y| x * y
}
// Approach 2: Curried function returning closures
fn make_greeting(prefix: &str) -> impl Fn(&str) -> Box<dyn Fn(&str) -> String + '_> + '_ {
move |name: &str| {
let owned_prefix = prefix.to_string();
let owned_name = name.to_string();
Box::new(move |suffix: &str| format!("{} {}{}", owned_prefix, owned_name, suffix))
}
}
// Simpler version:
fn greet(prefix: &str, name: &str, suffix: &str) -> String {
format!("{} {}{}", prefix, name, suffix)
}
// Approach 3: Higher-order + partial application
fn apply_twice(f: impl Fn(i32) -> i32, x: i32) -> i32 {
f(f(x))
}
fn compose<A, B, C>(f: impl Fn(B) -> C, g: impl Fn(A) -> B) -> impl Fn(A) -> C {
move |x| f(g(x))
}
fn main() {
let add5 = make_adder(5);
let double = make_multiplier(2);
println!("add5(3) = {}", add5(3));
println!("double(7) = {}", double(7));
let add10 = |x| apply_twice(&add5, x);
println!("add10(0) = {}", add10(0));
let add5_then_double = compose(make_multiplier(2), make_adder(5));
println!("add5_then_double(3) = {}", add5_then_double(3));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_application() {
let add5 = make_adder(5);
assert_eq!(add5(3), 8);
assert_eq!(add5(0), 5);
let double = make_multiplier(2);
assert_eq!(double(7), 14);
let triple = make_multiplier(3);
assert_eq!(triple(4), 12);
}
#[test]
fn test_apply_twice() {
let add5 = make_adder(5);
assert_eq!(apply_twice(&add5, 0), 10);
assert_eq!(apply_twice(&add5, 5), 15);
let double = make_multiplier(2);
assert_eq!(apply_twice(&double, 3), 12);
}
#[test]
fn test_compose() {
let add5_then_double = compose(make_multiplier(2), make_adder(5));
assert_eq!(add5_then_double(3), 16); // (3+5)*2
let double_then_add5 = compose(make_adder(5), make_multiplier(2));
assert_eq!(double_then_add5(3), 11); // 3*2+5
}
#[test]
fn test_greet() {
assert_eq!(greet("Hello", "World", "!"), "Hello World!");
}
}
(* 074: Currying and Partial Application *)
(* Approach 1: Natural currying *)
let add x y = x + y
let multiply x y = x * y
(* Partial application โ just give fewer arguments *)
let add5 = add 5
let double = multiply 2
let triple = multiply 3
(* Approach 2: Multi-argument currying *)
let make_greeting prefix name suffix =
Printf.sprintf "%s %s%s" prefix name suffix
let hello_greeting = make_greeting "Hello"
let hello_world = hello_greeting "World"
(* Approach 3: Higher-order with partial application *)
let apply_twice f x = f (f x)
let add10 = apply_twice add5
let quadruple = apply_twice double
let compose f g x = f (g x)
let add5_then_double = compose double add5
let double_then_add5 = compose add5 double
(* Tests *)
let () =
assert (add5 3 = 8);
assert (add5 0 = 5);
assert (double 7 = 14);
assert (triple 4 = 12);
assert (hello_world "!" = "Hello World!");
assert (add10 0 = 10);
assert (add10 5 = 15);
assert (quadruple 3 = 12);
assert (add5_then_double 3 = 16); (* (3+5)*2 = 16 *)
assert (double_then_add5 3 = 11); (* 3*2+5 = 11 *)
Printf.printf "โ All tests passed\n"
๐ Detailed Comparison
Core Insight
In OCaml, `let add x y = x + y` is actually `let add = fun x -> fun y -> x + y`. Partial application (`add 5`) returns a function. Rust functions aren't curried โ you use closures to simulate partial application.
OCaml Approach
- All functions are automatically curried
- `let add x y = x + y` โ `add 5` returns `fun y -> 5 + y`
- Free partial application of any prefix of arguments
Rust Approach
- Functions are NOT curried
- Closures capture variables: `let add5 = |y| 5 + y;`
- `move` closures for ownership transfer
- Can return closures with `impl Fn(T) -> U`
Comparison Table
| Feature | OCaml | Rust | ||
|---|---|---|---|---|
| Curried by default | Yes | No | ||
| Partial application | `f x` (give fewer args) | Closure capturing | ||
| Return function | Automatic | `impl Fn(T) -> U` | ||
| Closure syntax | `fun x -> ...` | `\ | x\ | ...` |