v2.5Sprachgrundlagen

Funktionen, Closures & Lambdas

Funktionen sind "First-Class Citizens" in Velisch - sie können Variablen zugewiesen und als Argumente übergeben werden.

Normale Funktionen

// Standard-Funktion
fn add(a: number, b: number): number {
    return a + b;
}

// Kurzform für einzeilige Rückgaben (implizit return)
fn multiply(a: number, b: number) => a * b;

// Funktion ohne Rückgabewert
fn printMessage(msg: string): void {
    log.info(msg);
}

// Async Funktion
async fn fetchData(url: string): Data {
    let response = await http.get(url);
    return response.json();
}

Optionale Parameter & Default-Werte

fn greet(name: string, greeting: string = "Hallo") {
    log.info(greeting + ", " + name + "!");
}

greet("Max");           // "Hallo, Max!"
greet("Lisa", "Moin");  // "Moin, Lisa!"

// Mit mehreren optionalen Parametern
fn createUser(
    name: string, 
    role: string = "user", 
    active: boolean = true
): User {
    return User { name, role, active };
}

Lambdas (Anonyme Funktionen)

Lambdas sind besonders kompakt und nützlich für Callbacks. Syntax: |param1, param2| expression

let numbers = [1, 2, 3];

// Lambda als Argument
let doubled = numbers.map(|n| n * 2);

// Lambda mit Block
let processed = numbers.map(|n| {
    let squared = n * n;
    return squared + 1;
});

// Lambda in Variable speichern
let doubler = |n| n * 2;
log.info(doubler(5)); // 10

// Lambda mit expliziten Typen
let add: fn(number, number) -> number = |a, b| a + b;

Closures

Closures sind Lambdas, die Variablen aus ihrem definierenden Scope "fangen" (capture). Dies ist extrem mächtig für Konfigurationen, Event-Handler oder funktionale Komposition.

fn createAdder(base: number): fn(number) -> number {
    // Die zurückgegebene Funktion "merkt" sich 'base'
    return |n| n + base;
}

let addFive = createAdder(5);
let addTen = createAdder(10);

log.info(addFive(2));  // 7
log.info(addTen(2));   // 12

// Closure mit mehreren gefangenen Variablen
fn createMultiplier(factor: number, offset: number): fn(number) -> number {
    return |n| (n * factor) + offset;
}

let transform = createMultiplier(2, 10);
log.info(transform(5)); // 20

Praktische Anwendungen

Event Handler

fn setupButton(buttonId: string, action: fn() -> void) {
    ui.onClick(buttonId, action);
}

let counter = 0;
setupButton("increment", || {
    counter = counter + 1;
    ui.update("count", counter);
});

Konfigurierbare Filter

fn createAgeFilter(minAge: number): fn(User) -> boolean {
    return |user| user.age >= minAge;
}

let adults = createAgeFilter(18);
let seniors = createAgeFilter(65);

let allUsers = db.findAll(User);
let adultUsers = allUsers.filter(adults);
let seniorUsers = allUsers.filter(seniors);

Middleware-Pattern

fn withLogging<T>(operation: fn() -> T): fn() -> T {
    return || {
        log.info("Operation started");
        let result = operation();
        log.info("Operation completed");
        return result;
    };
}

let fetchWithLogging = withLogging(|| {
    return http.get("https://api.example.com/data");
});

Performance

Stack-Allokation

Lokale Closures werden auf dem Stack alloziert

Inlining

Kleine Closures werden oft vom Compiler geinlined

Zero-Cost

Closures haben keine Laufzeit-Overhead gegenüber normalen Funktionen

Move Semantics

Captured Values werden effizient nach Rust übersetzt

Best Practices

Kleine Closures

Halte Closures kurz und fokussiert

Klare Namen

Benenne Closure-Variablen beschreibend

Capture minimieren

Fange nur Variablen, die du wirklich brauchst

Typen dokumentieren

Bei komplexen Closures explizite Typen angeben