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 are first-class values in Crush. This chapter covers function definition, calling, parameters, and return values.

Function Definition

Basic Function

fn greet() {
    io.print("Hello!");
}

With Parameters

fn greet(name: String) {
    io.print("Hello, " + name + "!");
}

With Return Value

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

Multiple Parameters

fn calculate(x: Int, y: Int, operation: String) -> Int {
    if operation == "add" {
        return x + y;
    } else if operation == "multiply" {
        return x * y;
    }
    return 0;
}

Function Calls

// No arguments
greet();

// With arguments
greet("Alice");

// Storing result
let sum = add(5, 3);

// Nested calls
let result = calculate(add(2, 3), 4, "multiply");

Return Values

Explicit Return

fn get_value() -> Int {
    return 42;
}

Multiple Return Points

fn check_sign(x: Int) -> String {
    if x > 0 {
        return "positive";
    } else if x < 0 {
        return "negative";
    }
    return "zero";
}

Void Functions

Functions without return values:

fn log_message(msg: String) {
    io.print("[LOG] " + msg);
    // No return statement
}

Parameters

Positional Parameters

fn create_user(name: String, age: Int, active: Bool) {
    // ...
}

create_user("Alice", 30, true);

Default Parameters (Future Feature)

fn greet(name: String = "Guest") {
    io.print("Hello, " + name);
}

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

Recursion

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

fn main() {
    let result = factorial(5);  // 120
    io.print(result);
}

Higher-Order Functions

Functions are first-class values and can be passed as arguments:

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

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

fn main() {
    let result = apply(double, 21);  // 42
}

Lambdas and Closures

Anonymous functions use the |params| { body } syntax (the Lambda AST node):

let double = |x| { return x * 2; };
let result = double(21);  // 42

// Single-expression form
let square = |x| => x * x;

Closures capture variables from the enclosing scope:

fn make_adder(x: Int) -> Function {
    return |y| { return x + y; };
}

fn main() {
    let add_five = make_adder(5);
    let result = add_five(10);  // 15
}

Async Functions

Use async/await for asynchronous operations. spawn launches a task concurrently:

async fn fetch_data(url: String) -> String {
    let response = await net.get(url);
    return response;
}

fn main() {
    let task = spawn fetch_data("https://api.example.com/data");
    let result = await task;
    io.print(result);
}

Best Practices

1. Use Type Hints

// Good
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

// Less clear
fn calculate(x, y) {
    return x + y;
}

2. Keep Functions Focused

// Good: Single responsibility
fn validate_email(email: String) -> Bool {
    // ...
}

fn send_email(to: String, subject: String, body: String) {
    // ...
}

3. Use Descriptive Names

// Good
fn calculate_total_price(items: Array, tax_rate: Float) -> Float {
    // ...
}

// Avoid
fn calc(x, y) {
    // ...
}

Next Steps