Partial Application
Partial application in NumFu lets you call a function with some of its arguments now, producing a new function that remembers those arguments and waits for the rest.
This works for both user-defined lambdas and built-in functions, and becomes even more powerful with the underscore (_
) placeholder syntax.
Currying and Partial Application
Automatic Currying
One of NumFu's most powerful features is automatic currying: when you provide fewer arguments than a function expects, you get back a new function.
let add = {x, y -> x + y} in
let add5 = add(5) in // Partial application
add5(3) // 8
Partially applied functions can also be inspected:
{x, y -> x + y}(5)
// {y -> 5 + y}
Progressive Application
You can apply arguments one at a time:
let multiply = {x, y, z -> x * y * z} in
let double = multiply(2) in // {y, z -> 2 * y * z}
let doubleBy3 = double(3) in // {z -> 2 * 3 * z}
doubleBy3(4) // 24
Too Many Arguments
Providing more arguments than parameters is an error:
let add = {x, y -> x + y} in
add(1, 2, 3)
// TypeError: Cannot apply 1 more arguments to non-callable result
The _
Placeholder
While currying applies arguments from left to right, the _
placeholder lets you skip arguments in any position and fill them in later.
max(_)([1, 2, 3]) // 3
max(_, 2, _)(1, 3) // 3
max(1, _, _)(2)(3) // 3
- Works in any position (start, middle, end)
- Works for both lambdas and built-in functions
- Multiple
_
placeholders allowed - You can fill multiple placeholders in one later call
Examples with custom functions
{a, b, c -> a + b + c}(_, 5, _)
// {a, c -> a + 5 + c}
{a, b, c -> a + b + c}(5, _)
// {b, c -> 5 + b + c}
Examples with built-ins
import max from "math"
max(_, _, _)(2, 3) // partially applied function
max(2, _)(3, 4) // 4
Placeholders remember fixed arguments and validate the rest when filled later.
Operators as Functions
In NumFu, operators like +
, -
, >
, ==
, and many others are internally just regular functions.
This means you can use them anywhere you can use a function — including partial application with _
.
This is especially useful for writing piping chains and simple helper functions without having to define lambdas explicitly.
_ + 1
// is equivalent to:
{x -> x + 1}
10 > _
// is equivalent to:
{x -> 10 > x}
Examples in Piping
[1, 2, 3] |> map(_, _ * 2);
// [2, 4, 6]
[5, 12, 3] |> filter(_, _ > 4) |> map(_, _ * 2)
// [10, 24]
Rest Parameters and _
Rest parameters (...name
) collect all remaining arguments into a list.
They combine seamlessly with placeholders:
{x, ...args -> args}(_, 1, 2)
// {x -> [1,2,2]}
{x, ...args -> args}(10, _, _, _)
// {...args -> args}
This also works for built-in functions that accept any number of arguments:
max(2, _)(3, 4) // 4
max(_, _, _)(2, 3) // partially applied function
Restrictions
You cannot use _
together with the spread (...
) operator when calling a function:
join(["a", "b"], ..._)
// TypeError: Cannot combine spread operator with argument placeholder
The spread operator requires fully evaluated values, not placeholders.
More Examples
import join from "std"
// Chain of partials
{a, b -> a * b}(2)
// {b -> 2 * b}
// Placeholder in middle, fill rest later
{a, b, c -> a + b * c}(_, 3)(4)
// {a -> a + 3 * 4}
{a, b, c -> a+b+c}(_, 5, _)
// {a,c -> a+5+c}
// With join
join(["x", "y"], _)("-")
// "x-y"
Rest parameters
import min, max from "math"
// Rest parameter with placeholders
{x, ...args -> args}(_, 1, 2)(5)
// [1, 2]
{x, ...args -> min(args) + x}(10, _, _, _)(1,2,3)
// 11
// Builtin function with multiple args in later call
max(5, _)(2, 8, 1) // 8
Combining with piping
import max from "math"
5 |> max(_, 10) // 10
// Creating a reusable partial function
let divideIt = {x, y -> x / y}(_, 2) in
10 |> divideIt; // 5
[5, 12, 3] |> filter(_, _ > 4) |> map(_, _ * 2)
// [10, 24]