Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Functions

Functions in Sage are defined at the top level and can be called from anywhere.

Defining Functions

fn greet(name: String) -> String {
    return "Hello, " ++ name ++ "!";
}

fn add(a: Int, b: Int) -> Int {
    return a + b;
}

Calling Functions

let message = greet("World");
let sum = add(1, 2);

Return Types

All functions must declare their return type:

fn double(n: Int) -> Int {
    return n * 2;
}

fn print_message(msg: String) -> Unit {
    print(msg);
    return;
}

Use Unit for functions that don’t return a meaningful value.

Generic Functions

Functions can have type parameters, making them work with any type:

fn identity<T>(x: T) -> T {
    return x;
}

fn swap<A, B>(pair: (A, B)) -> (B, A) {
    return (pair.1, pair.0);
}

let x = identity(42);       // T inferred as Int
let y = identity("hello");  // T inferred as String

See Generics for comprehensive coverage.

Recursion

Functions can call themselves:

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

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

Closures

Sage supports first-class functions and closures:

// Closure with typed parameters
let add = |x: Int, y: Int| x + y;

// Empty parameter closure
let get_value = || 42;

// Multi-statement closure with block
let greet = |name: String| {
    let msg = "Hello, " ++ name ++ "!";
    return msg;
};

Closure parameters require explicit type annotations.

Function Types

Use Fn(A, B) -> C to describe function types:

fn apply(f: Fn(Int) -> Int, x: Int) -> Int {
    return f(x);
}

let double = |x: Int| x * 2;
let result = apply(double, 21);  // 42

Higher-Order Functions

Functions can return closures:

fn make_multiplier(n: Int) -> Fn(Int) -> Int {
    return |x: Int| x * n;
}

let triple = make_multiplier(3);
let result = triple(10);  // 30

Fallible Functions

Functions that can fail are marked with fails:

fn risky_operation() -> Int fails {
    let value = try divine("Give me a number");
    return parse_int(value);
}

Callers must handle errors with try or catch:

agent Main {
    on start {
        let result = try risky_operation();
        yield(result);
    }

    on error(e) {
        yield(0);
    }
}

run Main;

Built-in Functions

Sage provides several built-in functions:

FunctionSignatureDescription
print(String) -> UnitPrint to console
str(T) -> StringConvert any value to string
len(List<T>) -> IntGet list or map length
push(List<T>, T) -> List<T>Append to list
join(List<String>, String) -> StringJoin strings
int_to_str(Int) -> StringConvert int to string
str_contains(String, String) -> BoolCheck substring
sleep_ms(Int) -> UnitSleep for milliseconds
map_get(Map<K,V>, K) -> Option<V>Get value from map
map_set(Map<K,V>, K, V) -> UnitSet key-value in map
map_has(Map<K,V>, K) -> BoolCheck if key exists
map_delete(Map<K,V>, K) -> UnitRemove key from map
map_keys(Map<K,V>) -> List<K>Get all keys as list
map_values(Map<K,V>) -> List<V>Get all values as list

Example

fn summarize_list(items: List<String>) -> String {
    let count = len(items);
    let joined = join(items, ", ");
    return "Found " ++ str(count) ++ " items: " ++ joined;
}

agent Main {
    on start {
        let result = summarize_list(["apple", "banana", "cherry"]);
        print(result);
        yield(0);
    }
}

run Main;

Output:

Found 3 items: apple, banana, cherry