Lambda Functions
July 7, 2026 · View on GitHub
This document covers Lambda's function system, including pure functional (fn) and procedural (pn) functions, parameters, closures, and higher-order functions.
Related Documentation:
- Lambda Reference — Language overview
- Lambda Sys Func Reference — Built-in system functions
- Lambda Expressions — Expressions and statements
- Lambda Type System — Type annotations
- Lambda Procedural — Procedural statements (
var,while, assignment)
Table of Contents
- Function Overview
- Function Declarations
- Function Parameters
- Function Calls
- Method-Style Calls
- Closures
- Higher-Order Functions
- Procedural Functions
Function Overview
Lambda supports two kinds of functions:
| Kind | Keyword | Characteristics |
|---|---|---|
| Functional | fn | Pure, immutable, expression-based |
| Procedural | pn | Mutable state, side effects, imperative |
Quick Comparison
// Pure function — no side effects
fn double(x: int) => x * 2
// Procedural function — can have side effects
pn save_result(data) {
output(data, "./temp/output.json")
}
Function Declarations
Function of Statements
// Statements as function body
fn add(a: int, b: int) int {
a + b
}
fn greet(name: string) string {
"Hello, "
name
"!"
}
fn process(data) {
let filtered = data where ~ > 0;
let doubled = filtered | ~ * 2;
doubled
}
Function of Expression
// Single expression body
fn multiply(x: int, y: int) => x * y
fn square(n: int) => n ** 2
Anonymous Functions
// Lambda expressions
(x: int) => x * 2
fn (x: int, y: int) { ... }
// With inferred types
(x) => x * 2
Function Parameters
Required Parameters
Parameters with type annotations are required:
fn greet(name: string) => "Hello, " ++ name
fn add(a: int, b: int) => a + b
add(5, 3) // 8
add(5) // Error: missing required parameter
Optional Parameters
Use ? before the type to make a parameter optional:
fn greet(name: string, title?: string) => {
if (title) title ++ " " ++ name
else name
}
greet("Alice") // "Alice"
greet("Alice", "Dr.") // "Dr. Alice"
Note: a?: T means the parameter is optional (may be null). This is different from a: T? where the type is nullable but the parameter is required.
Default Parameter Values
Parameters can have default values:
fn greet(name = "World") => "Hello, " ++ name ++ "!"
fn power(base: int, exp: int = 2) => base ** exp
greet() // "Hello, World!"
greet("Lambda") // "Hello, Lambda!"
power(3) // 9 (3**2)
power(2, 10) // 1024 (2**10)
Default expressions can reference earlier parameters:
fn make_rect(width: int, height = width) => {
width: width, height: height
}
make_rect(10) // {width: 10, height: 10}
make_rect(10, 20) // {width: 10, height: 20}
Named Arguments
Arguments can be passed by name in any order:
fn create_user(name: string, age: int, active: bool = true) => {
name: name, age: age, active: active
}
// All equivalent:
create_user("Alice", 30, true)
create_user(name: "Alice", age: 30, active: true)
create_user(age: 30, name: "Alice") // Order independent
// Skip optional parameters
create_user("Bob", 25) // active defaults to true
Rules:
- Positional arguments must come before named arguments
- Named arguments can appear in any order
- Cannot provide the same argument both positionally and by name
Variadic Parameters
Use ... to accept any number of additional arguments:
fn sum_all(...) => sum(varg())
fn printf(fmt: string, ...) => format(fmt, varg())
sum_all(1, 2, 3, 4, 5) // 15
sum_all() // 0
printf("%s is %d", "x", 42) // "x is 42"
Access variadic arguments with varg():
| Call | Returns |
|---|---|
varg() | List of all variadic arguments |
varg(n) | The nth variadic argument (0-indexed) |
fn first_or_default(default, ...) => {
if (len(varg()) > 0) varg(0)
else default
}
first_or_default(0, 1, 2, 3) // 1
first_or_default(0) // 0
Parameter Order
Parameters must be declared in this order:
required → optional (?) → defaults → variadic (...)
// Valid
fn valid(req: int, opt?: int, def: int = 10, ...) => ...
// Invalid
fn invalid(opt?: int, req: int) => ... // Error
Parameter Mismatch Handling
| Situation | Behavior |
|---|---|
| Missing required argument | Compile-time error |
| Missing optional argument | Filled with null |
| Missing default argument | Evaluates default expression |
| Extra arguments (no variadic) | Warning, discarded |
Function Calls
Basic Calls
add(5, 3)
greet("Alice")
calculate(2.5, 3.0, "add")
Chained Calls
// Direct chaining
process(filter(sort(data)))
// Method-style chaining (preferred)
data.sort().filter(x => x > 0).process()
Partial Application
// Create specialized functions
fn add(a: int, b: int) => a + b
let add5 = (x) => add(5, x)
add5(3) // 8
Method-Style Calls
System functions can be called using method syntax:
// Traditional prefix style
len(arr)
sum([1, 2, 3])
slice("hello", 0, 3)
// Method style (equivalent)
arr.len()
[1, 2, 3].sum()
"hello".slice(0, 3)
// Array operations
[3, 1, 4, 1, 5].sort() // [1, 1, 3, 4, 5]
[3, 1, 4, 1, 5].sum() // 14
[3, 1, 4, 1, 5].unique() // [3, 1, 4, 5]
// Chained
[5, 3, 1, 4, 2].sort().reverse() // [5, 4, 3, 2, 1]
// Type conversion
42.string() // "42"
"123".int() // 123
3.14.floor() // 3
Supported Functions
| Category | Prefix Style | Method Style |
|---|---|---|
| Type | len(arr) | arr.len() |
| Type | type(val) | val.type() |
| Type | string(42) | 42.string() |
| String | slice(s, 0, 5) | s.slice(0, 5) |
| String | contains(s, "x") | s.contains("x") |
| Collection | reverse(arr) | arr.reverse() |
| Collection | sort(arr) | arr.sort() |
| Collection | take(arr, 3) | arr.take(3) |
| Stats | sum(nums) | nums.sum() |
| Stats | avg(nums) | nums.avg() |
| Math | abs(x) | x.abs() |
| Math | math.sqrt(x) | x |> math.sqrt |
Method Chaining
Method syntax enables fluent operations:
// Chained method calls
let result = data
.filter(x => x > 0)
.map(x => x * 2)
.sort()
.take(10)
.sum()
// Equivalent nested calls (harder to read)
let result = sum(take(sort(map(filter(data, x => x > 0), x => x * 2)), 10))
Closures
Closures are functions that capture variables from their enclosing scope.
Basic Closure
let multiplier = 3
let triple = (x) => x * multiplier
triple(5) // 15
Capturing Multiple Variables
fn make_linear(slope: int, intercept: int) {
fn eval(x) => slope * x + intercept
eval
}
let line = make_linear(2, 10)
line(5) // 20 (2*5 + 10)
line(10) // 30 (2*10 + 10)
Nested Closures
fn level1(a) {
fn level2(b) {
fn level3(c) {
fn level4(d) => a + b + c + d
level4
}
level3
}
level2
}
level1(1)(2)(3)(4) // 10
Closures with Let Bindings
fn make_counter(start: int) {
let initial = start * 2
fn count(step) => initial + step
count
}
let counter = make_counter(5)
counter(3) // 13 (5*2 + 3)
Closure Captures Are Immutable Snapshots
Closures capture values by snapshot. A nested fn or pn may read a captured binding, but it cannot assign to the captured binding or mutate through it with an interior write such as xs[0] = value. Mutable state that must be updated by a helper should be passed explicitly as a var parameter, or returned as a new value.
pn main() {
var base = 40
pn add_two() {
base + 2 // read-only capture
}
let f = add_two
print(f()) // 42
}
pn bump_first(var xs: any[]) {
xs[0] = 42
}
pn main() {
var xs: any[] = [1, 2, 3]
bump_first(xs)
print(xs[0]) // 42
}
Capture and mutability semantics:
- Captures are by value: each closure gets a snapshot at creation time.
- Captured bindings are read-only, including interior writes through arrays, maps, elements, and objects.
varparameters are explicit inout parameters; callers must pass a mutablevarbinding.- A
varparameter is invariant: the argument type must match the parameter type exactly. For covariant array assignment, declare the local binding with the wider type, such asvar xs: any[] = [1, 2, 3]. - Named closures must be called through a variable (
let f = name; f()) to pass the environment.
Higher-Order Functions
Functions that take or return functions.
Functions as Arguments
fn apply(f, x) => f(x)
fn map_array(arr, f) => (for (x in arr) f(x))
apply((x) => x * 2, 5) // 10
map_array([1, 2, 3], (x) => x ** 2) // [1, 4, 9]
Functions as Return Values
fn compose(f, g) => (x) => g(f(x))
let add1 = (x) => x + 1
let double = (x) => x * 2
let add1_then_double = compose(add1, double)
add1_then_double(5) // 12 (double(add1(5)))
Common Higher-Order Patterns
// Filter
fn filter(arr, pred) => arr where pred(~)
// Map
fn map(arr, f) => arr | f(~)
// Reduce/Fold
fn reduce(arr, init, f) => {
let result = init
for (x in arr) {
result = f(result, x)
}
result
}
// Usage
filter([1, 2, 3, 4, 5], (x) => x > 2) // [3, 4, 5]
map([1, 2, 3], (x) => x * 2) // [2, 4, 6]
reduce([1, 2, 3, 4], 0, (a, b) => a + b) // 10
Procedural Functions
Procedural functions (pn) allow mutable state, side effects, and imperative control flow. See Lambda Procedural Programming for full documentation.
System Functions Reference
Lambda provides extensive built-in functions. See Lambda_Sys_Func.md for complete documentation.
Function Categories
| Category | Examples | Description |
|---|---|---|
| Type | type, len, int, float, string | Type conversion and inspection |
| Math | abs, round, floor, ceil, sqrt, log | Mathematical operations |
| Statistical | sum, avg, median, variance | Statistical analysis |
| Collection | slice, reverse, sort, unique | Collection manipulation |
| String | upper, lower, trim, split, join | String operations |
| I/O | input, format, print, output, exists | Input/output |
| Date/Time | datetime, date, time, today, now | Date and time |
This document covers Lambda's function system. For built-in function details, see Lambda_Sys_Func.md. For expressions, see Lambda Expressions. For procedural programming, see Lambda Procedural.