Skip to main content

Functions

As a functional programming language, NumFu treats functions as first-class values that can be created, passed around, and composed to build complex programs from simple parts.


Function Basics​

Creating Simple Functions​

In NumFu, functions are created using lambda expressions with the syntax {parameters -> body}:

{x -> x + 1}                    // Function that adds 1
{x -> x * x} // Function that squares a number
{name -> "Hello, " + name} // Function that creates a greeting

Calling Functions​

Call functions using parentheses:

let square = {x -> x * x} in square(5)     // 25
let greet = {name -> "Hello, " + name} in greet("NumFu") // "Hello, NumFu"

Inline Function Calls​

You can call lambda expressions directly:

{x -> x + 1}(10)               // 11
{x, y -> x * y}(3, 4) // 12

Multiple Parameters​

Multiple Arguments​

Functions can accept multiple parameters separated by commas:

{x, y -> x + y}                // Addition function
{a, b, c -> a * b + c} // Linear function
let say = {name, age -> format("{} is {} years old", name, age)} in
say("John", 42)
// John is 42 years old

Recursive Functions​

Named functions can call themselves recursively:

let fibonacci = {n ->
if n <= 1 then n
else fibonacci(n - 1) + fibonacci(n - 2)
}

fibonacci(10) // 55

You can control the maximum recursion depth using the CLI argument --rec-depth (see CLI reference).

Tail Call Optimization​

NumFu automatically optimizes tail-recursive function calls, allowing you to write recursive algorithms without running into stack overflow or performance issues.

A function call is in "tail position" when it's the last operation before returning. NumFu can optimize these calls to use constant memory instead of growing the call stack:

// Tail-recursive factorial (optimized)
let factorial = {n, acc ->
if n <= 0 then acc
else factorial(n - 1, acc * n)
}

// Non tail-recursive factorial (not optimized)
let factorial_slow = {n ->
if n <= 0 then 1
else n * factorial_slow(n - 1) // multiplication happens after the call
}

The tail-recursive version works, but the non-optimized version fails with large values:

>>> factorial(10000, 1); factorial_slow(10000)
2.84625968091707e+35659
[at REPL:1:?]
RecursionError: maximum recursion depth exceeded

You can control the maximum maximum number of tail-call iterations using the CLI argument --iter-depth (see CLI reference).


Rest Parameters​

Collecting Extra Arguments​

Use ...paramName to collect remaining arguments into a list:

{first, ...rest -> [first, rest]}(1, 2, 3, 4, 5)
// [1, [2, 3, 4, 5]]

{...args -> length(args)}(1, 2, 3) // 3

Each function can only have a single rest parameter which must come at the end.

Example​

import length, slice from "std"

let sum = {...numbers ->
let helper = {nums, acc ->
if length(nums) == 0 then acc
else helper(slice(nums, 1, -1), acc + nums[0])
} in helper(numbers, 0)
} in
sum(1, 2, 3, 4, 5) // 15

Higher-Order Functions​

Functions That Accept Functions​

Functions can take other functions as parameters:

let twice = {f, x -> f(f(x))} in
let add1 = {x -> x + 1} in
twice(add1, 5) // 7 (add1(add1(5)))

Functions That Return Functions​

Functions can return other functions:

let makeAdder = {n -> {x -> x + n}} in
let add10 = makeAdder(10) in
add10(5) // 15

Function Factories​

Create specialized functions using function factories:

let makePowerFunction = {exponent ->
{base -> base ^ exponent}
} in
let square = makePowerFunction(2) in
let cube = makePowerFunction(3) in
[square(4), cube(3)] // [16, 27]

Closures​

Capturing Environment​

Functions capture variables from their surrounding environment:

let multiplier = 10 in
let scale = {x -> x * multiplier} in
scale(5) 50

Closures Preserve Values​

Even when the original scope ends, the captured values remain:

let makeCounter = {start ->
let current = start in
{increment -> current + increment}
} in
let counter = makeCounter(100) in
counter(5) // 105

Examples​

Configurable Greeting​

// Create a configurable greeting function
let makeGreeting = {greeting, punctuation, name ->
greeting + ", " + name + punctuation
} in
let casual = makeGreeting("Hi") in
let casualExcited = casual("!") in
casualExcited("Alice") // "Hi, Alice!"

Conditional Function Selection​

Choose functions based on conditions:

let operation = "square" in
let processor = if operation == "square" then {x -> x * x}
else if operation == "double" then {x -> x * 2}
else {x -> x} in
processor(6) // 36

Mathematical Functions​

import sqrt from "math"

// Distance between two points
let distance = {x1, y1, x2, y2 ->
let dx = x2 - x1, dy = y2 - y1 in
sqrt(dx^2 + dy^2)
} in
distance(0, 0, 3, 4) // 5

Function Composition Function​

// Compose multiple functions
let compose = {f, g -> {x -> f(g(x))}} in

let add1 = {x -> x + 1} in
let double = {x -> x * 2} in

compose(double, add1)(5) // 12 (double(add1(5)))

Higher-Order Functions​

// Create a function that applies another function twice
let applyTwice = {f, x -> f(f(x))} in
let increment = {x -> x + 1} in
applyTwice(increment, 10) // 12
// Create a function that creates multiplier functions
let makeMultiplier = {factor -> {x -> x * factor}} in
let triple = makeMultiplier(3) in
triple(7) // 21

Recursive Functions​

// Factorial function
let factorial = {n ->
if n <= 1 then 1
else n * factorial(n - 1)
} in
factorial(5) // 120
// Square root using Newton's method
let sqrt = {x ->
let improve = {guess -> (guess + x / guess) / 2} in
let iterate = {guess, n ->
if n <= 0 then guess
else iterate(improve(guess), n - 1)
} in iterate(x / 2, 10)
} in sqrt(25) // 5