๐Ÿฆ€ Functional Rust

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

FeatureOCamlRust
Curried by defaultYesNo
Partial application`f x` (give fewer args)Closure capturing
Return functionAutomatic`impl Fn(T) -> U`
Closure syntax`fun x -> ...``\x\...`