Skip to main content
Functions are first-class values in Walrus, meaning they can be assigned to variables, passed as arguments, and returned from other functions. This guide covers all aspects of working with functions.

Function declaration

Declare functions using the fn keyword:
fn greet : name {
    println(f"Hello, {name}!");
}

greet("Alice");  // Hello, Alice!

Multiple parameters

fn add : a, b {
    return a + b;
}

let result = add(5, 3);
println(result);  // 8

No parameters

fn say_hello : {
    return "Hello, World!";
}

let message = say_hello();
println(message);

Return values

Use the return keyword to return values:
fn multiply : x, y {
    return x * y;
}

let product = multiply(4, 7);
println(product);  // 28

Implicit void return

Functions without a return statement return void:
fn print_message : msg {
    println(msg);
    // Implicitly returns void
}

let result = print_message("Hi");
println(type(result));  // void

Early returns

fn check_value : n {
    if n < 0 {
        return "negative";
    }
    if n == 0 {
        return "zero";
    }
    return "positive";
}

println(check_value(-5));  // negative
println(check_value(0));   // zero
println(check_value(10));  // positive

Anonymous functions

Create functions without names using the : syntax:
let square = : x {
    return x * x;
};

let result = square(7);
println(result);  // 49

Single expression lambdas

let double = : x {
    return x * 2;
};

let cube = : x {
    return x * x * x;
};

println(double(5));  // 10
println(cube(3));    // 27

First-class functions

Functions can be assigned to variables, passed as arguments, and returned from other functions.

Assigning functions to variables

fn add : a, b {
    return a + b;
}

// Assign function to variable
let operation = add;

// Call through variable
let result = operation(10, 5);
println(result);  // 15

Passing functions as arguments

fn apply_twice : func, value {
    return func(func(value));
}

fn double : x {
    return x * 2;
}

let result = apply_twice(double, 5);
println(result);  // 20 (5 * 2 * 2)

Returning functions

fn make_multiplier : n {
    return : x {
        return x * n;
    };
}

let times_three = make_multiplier(3);
let times_five = make_multiplier(5);

println(times_three(10));  // 30
println(times_five(10));   // 50

Higher-order functions

Functions that take other functions as parameters:
fn map : func, list {
    let result = [];
    for item in list {
        result.push(func(item));
    }
    return result;
}

fn square : x {
    return x * x;
}

let numbers = [1, 2, 3, 4, 5];
let squared = map(square, numbers);
println(squared);  // [1, 4, 9, 16, 25]

Filter function

fn filter : predicate, list {
    let result = [];
    for item in list {
        if predicate(item) {
            result.push(item);
        }
    }
    return result;
}

fn is_even : n {
    return n % 2 == 0;
}

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let evens = filter(is_even, numbers);
println(evens);  // [2, 4, 6, 8, 10]

Reduce function

fn reduce : func, list, initial {
    let accumulator = initial;
    for item in list {
        accumulator = func(accumulator, item);
    }
    return accumulator;
}

fn add : a, b {
    return a + b;
}

let numbers = [1, 2, 3, 4, 5];
let sum = reduce(add, numbers, 0);
println(sum);  // 15

Recursion

Functions can call themselves:

Factorial

fn factorial : n {
    if n <= 1 {
        return 1;
    }
    return n * factorial(n - 1);
}

println(factorial(5));   // 120
println(factorial(10));  // 3628800

Fibonacci

fn fibonacci : n {
    if n <= 1 {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

println(fibonacci(10));  // 55
Walrus supports tail call optimization for recursive functions, improving performance for certain recursive patterns.

Recursive list operations

fn sum_list : lst {
    if len(lst) == 0 {
        return 0;
    }
    return lst[0] + sum_list(lst[1..(-1)]);
}

let numbers = [10, 20, 30, 40, 50];
println(sum_list(numbers));  // 150

Quicksort

fn quicksort : arr {
    if len(arr) <= 1 {
        return arr;
    }
    
    let pivot = arr[0];
    let less = [];
    let equal = [];
    let greater = [];
    
    for x in arr {
        if x < pivot {
            less.push(x);
        } else if x == pivot {
            equal.push(x);
        } else {
            greater.push(x);
        }
    }
    
    return quicksort(less) + equal + quicksort(greater);
}

let unsorted = [64, 34, 25, 12, 22, 11, 90, 88];
let sorted = quicksort(unsorted);
println(sorted);  // [11, 12, 22, 25, 34, 64, 88, 90]

Nested functions

You can define functions inside other functions:
fn outer : x {
    fn inner : y {
        return x + y;
    }
    
    return inner(10);
}

println(outer(5));  // 15

Function composition

fn compose : f, g {
    return : x {
        return f(g(x));
    };
}

fn add_one : x {
    return x + 1;
}

fn double : x {
    return x * 2;
}

// double(add_one(x))
let double_then_add = compose(double, add_one);
println(double_then_add(5));  // 12 ((5 + 1) * 2)

Practical examples

Mathematical sequences

fn generate_sequence : n, func {
    let result = [];
    for i in 0..n {
        result.push(func(i));
    }
    return result;
}

fn square : x {
    return x * x;
}

fn cube : x {
    return x * x * x;
}

let squares = generate_sequence(10, square);
let cubes = generate_sequence(10, cube);

println(squares);  // [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
println(cubes);    // [0, 1, 8, 27, 64, 125, 216, 343, 512, 729]

String processing

fn reverse_string : s {
    if len(s) <= 1 {
        return s;
    }
    return reverse_string(s[1..(-1)]) + s[0];
}

fn is_palindrome : s {
    return s == reverse_string(s);
}

let word1 = "racecar";
let word2 = "hello";

println(f"'{word1}' reversed: {reverse_string(word1)}");
println(f"'{word1}' is palindrome: {is_palindrome(word1)}");  // true
println(f"'{word2}' is palindrome: {is_palindrome(word2)}");  // false

Prime number generator

fn is_prime : n {
    if n < 2 {
        return false;
    }
    let i = 2;
    while i * i <= n {
        if n % i == 0 {
            return false;
        }
        i = i + 1;
    }
    return true;
}

fn get_primes : max {
    let primes = [];
    for i in 2..max {
        if is_prime(i) {
            primes.push(i);
        }
    }
    return primes;
}

let primes = get_primes(50);
println(primes);
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]

Currying

fn curry_add : a {
    return : b {
        return a + b;
    };
}

let add_five = curry_add(5);
let add_ten = curry_add(10);

println(add_five(3));   // 8
println(add_ten(3));    // 13

Common patterns

fn map : func, list {
    let result = [];
    for item in list {
        result.push(func(item));
    }
    return result;
}

let numbers = [1, 2, 3, 4, 5];
let doubled = map(: x { return x * 2; }, numbers);
println(doubled);  // [2, 4, 6, 8, 10]

Best practices

Each function should do one thing well:
// Good: separate concerns
fn validate_email : email {
    return "@" in email and "." in email;
}

fn send_email : to, subject, body {
    if not validate_email(to) {
        return false;
    }
    // Send email logic...
    return true;
}

// Avoid: doing too much in one function
fn process_and_send : email, subject, body {
    // Validation, processing, sending all mixed together
}
Function names should clearly indicate what they do:
// Good
fn calculate_total_price : items { }
fn is_valid_password : password { }
fn get_user_by_id : id { }

// Avoid
fn calc : items { }
fn check : password { }
fn get : id { }
Be mindful of stack depth with recursive functions:
// Tail-recursive (optimized)
fn factorial_tail : n, acc {
    if n <= 1 {
        return acc;
    }
    return factorial_tail(n - 1, n * acc);
}

fn factorial : n {
    return factorial_tail(n, 1);
}

// Not tail-recursive (can overflow stack)
fn factorial_simple : n {
    if n <= 1 {
        return 1;
    }
    return n * factorial_simple(n - 1);  // Operation after recursive call
}

Next steps

Structs

Learn about struct definitions and static methods

Format strings

Master f-string interpolation for dynamic strings

Build docs developers (and LLMs) love