Inhaltsverzeichnis

Funktionen (vertieft)

Funktionen sind der zentrale Baustein jedes Lyx-Programms. Diese Seite geht über die Grunddeklaration hinaus und behandelt Tupel-Rückgaben, anonyme Funktionen, Funktionstypen, Higher-Order Functions und den Pipe-Operator.

→ Einführung: Erste Schritte · Syntax: Sprachsyntax · Methoden: OOP & Klassen · Generics: Generics & Traits


1. Grunddeklaration

fn Add(a: int64, b: int64): int64 {
    return a + b;
}

// Kein Rückgabewert
fn Log(msg: pchar) {
    PrintStr(msg);
    PrintStr("\n");
}

// Öffentlich sichtbar (exportiert)
pub fn GetVersion(): pchar {
    return "1.0.0";
}

Alle Funktionen ohne pub sind privat — sie sind nur innerhalb derselben Unit sichtbar. Das ist die strenge Voreinstellung; explizites Exportieren verhindert unkontrollierte Kopplung zwischen Modulen.


2. Tupel-Rückgabe — mehrere Werte

Funktionen können mehr als einen Wert zurückgeben, indem der Rückgabetyp als Tupel angegeben wird. Das ersetzt Out-Parameter und verhindert, dass Fehlerinformationen über Ausnahmen transportiert werden müssen.

Syntax

fn Divide(a: int64, b: int64): (int64, bool) {
    if (b = 0) { return (0, false); }
    return (a / b, true);
}

Rückgabetyp: (int64, bool) — ein Tupel aus zwei Typen.
Rückgabewert: (Ausdruck1, Ausdruck2) — beide Werte in runden Klammern, kommagetrennt.

Destrukturierung beim Aufruf

fn main(): int64 {
    var (result, ok): (int64, bool) := Divide(10, 3);
    if (ok) {
        PrintInt(result);   // 3
        PrintStr("\n");
    }

    // Kurzform ohne explizite Typangabe — Typen werden inferiert
    var (q, ok2) := Divide(7, 0);
    if (ok2 = false) {
        PrintStr("Division durch null\n");
    }

    return 0;
}

Dreiwertige Ergebnisse

Tupel-Rückgaben sind nicht auf zwei Elemente beschränkt:

fn ParseCoord(input: pchar): (f64, f64, bool) {
    // ... Parser-Logik ...
    return (lat, lon, true);
}

fn main(): int64 {
    var (lat, lon, valid) := ParseCoord("48.137,11.576");
    if (valid) {
        PrintF64(lat); PrintStr(", "); PrintF64(lon); PrintStr("\n");
    }
    return 0;
}

Tupel-Rückgabe vs. struct

Situation Empfehlung
Zwei eng zusammengehörige Werte (Ergebnis + Fehlerflag) Tupel-Rückgabe
Mehr als drei Werte Struct definieren, als Wert zurückgeben
Rückgabe soll benannt und dokumentiert sein Struct — Feldnamen machen den Code lesbarer
Temporäres Ergebnis in einer Funktion Tupel — weniger Overhead als ein Typ zu definieren

3. Anonyme Funktionen

Lyx erlaubt Funktionsliterale — also Funktionen ohne Namen, die direkt als Ausdruck geschrieben werden. Sie folgen der gleichen Syntax wie benannte Funktionen, tragen aber keinen Bezeichner.

Syntax

fn(param1: Typ1, param2: Typ2): RückgabeTyp {
    return ...;
}

Inline als Argument

import std.io;

fn Apply(value: int64, fn transform: fn(int64): int64): int64 {
    return transform(value);
}

fn main(): int64 {
    var result: int64 := Apply(21, fn(x: int64): int64 {
        return x * 2;
    });
    PrintInt(result);   // 42
    PrintStr("\n");
    return 0;
}

Als Variable speichern

Eine anonyme Funktion kann mit let oder var gebunden werden:

let double: fn(int64): int64 := fn(x: int64): int64 {
    return x * 2;
};

let greet: fn(pchar): void := fn(name: pchar): void {
    PrintStr("Hallo, ");
    PrintStr(name);
    PrintStr("\n");
};

fn main(): int64 {
    PrintInt(double(21));   // 42
    PrintStr("\n");
    greet("Lyx");           // Hallo, Lyx
    return 0;
}

Hinweis: Anonyme Funktionen in Lyx sind keine Closures im Sinne von funktionalen Sprachen — sie erfassen keine Variablen aus dem umgebenden Scope. Wenn Zustand übergeben werden muss, wird er explizit als Parameter mitgegeben. Das vermeidet versteckte Abhängigkeiten und ist für Safety-kritischen Code (DO-178C, MISRA) zwingend.

4. Funktionstypen

Ein Funktionstyp beschreibt die Signatur einer Funktion — welche Parameter sie nimmt und welchen Typ sie zurückgibt. Mit type lässt sich ein benannter Alias für einen Funktionstyp anlegen.

Syntax

type TypName = fn(Param1Typ, Param2Typ): RückgabeTyp;

Beispiele

// Vergleichsfunktion für zwei int64-Werte — gibt Ordnung zurück
type Comparator = fn(a: int64, b: int64): int32;

// Transformationsfunktion: int64 → int64
type Transformer = fn(value: int64): int64;

// Ereignis-Handler ohne Rückgabewert
type EventHandler = fn(event_id: int32, data: int64): void;

Benannte Typen verbessern die Lesbarkeit von Parameterlisten erheblich:

// Ohne Alias — schwer zu lesen
fn Sort(arr: int64, n: int64, cmp: fn(int64, int64): int32): void { ... }

// Mit Alias — sofort klar
type Comparator = fn(a: int64, b: int64): int32;
fn Sort(arr: int64, n: int64, cmp: Comparator): void { ... }

Benannte Funktionen können überall dort übergeben werden, wo ein passender Funktionstyp erwartet wird — einfach durch Angabe des Namens ohne ():

fn CompareAsc(a: int64, b: int64): int32 {
    if (a < b) { return -1i32; }
    if (a > b) { return  1i32; }
    return 0i32;
}

fn CompareDesc(a: int64, b: int64): int32 {
    return CompareAsc(b, a);   // Reihenfolge getauscht
}

fn main(): int64 {
    var cmp: Comparator := CompareAsc;   // Funktion als Wert
    PrintInt(cmp(3, 7) as int64);        // -1
    PrintStr("\n");

    cmp := CompareDesc;
    PrintInt(cmp(3, 7) as int64);        // 1
    PrintStr("\n");

    return 0;
}


5. Higher-Order Functions

Eine Higher-Order Function (HOF) ist eine Funktion, die eine andere Funktion als Parameter entgegennimmt oder eine Funktion als Ergebnis zurückgibt.

Funktion als Parameter

Der Parameter wird mit dem Präfix fn und dem Funktionstyp deklariert:

fn paramName: fn(ArgTyp): RückgabeTyp

Vollständiges Beispiel:

import std.io;

// Apply: wendet transform auf value an und gibt das Ergebnis zurück
fn Apply(value: int64, fn transform: fn(int64): int64): int64 {
    return transform(value);
}

// Für jeden Index in [0, n) callback aufrufen
fn ForEach(n: int64, fn callback: fn(int64): void): void {
    var i: int64 := 0;
    while (i < n) limit(1000000) {
        callback(i);
        i := i + 1;
    }
}

// Akkumuliert f(i) für alle i in [0, n)
fn Accumulate(n: int64, initial: int64, fn fold: fn(int64, int64): int64): int64 {
    var acc: int64 := initial;
    var i: int64 := 0;
    while (i < n) limit(1000000) {
        acc := fold(acc, i);
        i := i + 1;
    }
    return acc;
}

fn Square(x: int64): int64  { return x * x; }
fn PrintIdx(i: int64): void { PrintInt(i); PrintStr(" "); }
fn Sum(acc: int64, val: int64): int64 { return acc + val; }

fn main(): int64 {
    PrintInt(Apply(7, Square));      // 49
    PrintStr("\n");

    ForEach(5, PrintIdx);            // 0 1 2 3 4
    PrintStr("\n");

    PrintInt(Accumulate(5, 0, Sum)); // 0+1+2+3+4 = 10
    PrintStr("\n");

    return 0;
}

HOF mit Generics

Funktionsparameter lassen sich mit Generics kombinieren:

fn MapApply<In, Out>(value: In, fn transform: fn(In): Out): Out {
    return transform(value);
}

fn main(): int64 {
    var doubled: int64 := MapApply<int64, int64>(21, fn(x: int64): int64 {
        return x * 2;
    });
    PrintInt(doubled);   // 42
    PrintStr("\n");
    return 0;
}

→ Ausführliche Generics-Syntax: Generics & Traits

Dispatch-Tabelle

Funktionstypen lassen sich in Arrays speichern — das ist das Lyx-Äquivalent einer vtable ohne Klassen:

type Handler = fn(data: int64): void;

fn OnConnect(data: int64): void    { PrintStr("connect\n"); }
fn OnDisconnect(data: int64): void { PrintStr("disconnect\n"); }
fn OnMessage(data: int64): void    { PrintStr("message\n"); }

con CONNECT:    int64 := 0;
con DISCONNECT: int64 := 1;
con MESSAGE:    int64 := 2;

fn main(): int64 {
    var dispatch: [3]Handler := [OnConnect, OnDisconnect, OnMessage];

    var event: int64 := MESSAGE;
    if (event >= 0 & event < 3) {
        dispatch[event](0);   // message
    }

    return 0;
}

Funktionen an Threads übergeben

std.thread erwartet den Funktionszeiger als int64. Das ist die einzige Ausnahme von der typisierten Übergabe — es liegt an der C-POSIX-Schnittstelle unter der Haube:

import std.thread;
import std.io;

fn Worker(arg: int64): int64 {
    PrintStr("Thread läuft\n");
    return 0;
}

fn main(): int64 {
    var t: Thread := ThreadCreate(Worker as int64, 0);
    ThreadJoin(t);
    return 0;
}

→ Vollständige Thread-Dokumentation: Threads & Nebenläufigkeit


6. Pipe-Operator

Der Pipe-Operator |> leitet den Wert auf der linken Seite als erstes Argument an die Funktion auf der rechten Seite weiter. Er macht Transformationsketten ohne verschachtelte Klammern lesbar.

Syntax

wert |> Funktion(weitereArgumente)
// entspricht: Funktion(wert, weitereArgumente)

Beispiel: Wert-Transformation

import std.math;
import std.io;

fn Clamp(val: f64, lo: f64, hi: f64): f64 {
    if (val < lo) { return lo; }
    if (val > hi) { return hi; }
    return val;
}

fn main(): int64 {
    // Ohne Pipe — schwer zu lesen bei tiefer Schachtelung
    var r1: f64 := Round(Clamp(Abs(-3.7), 0.0, 3.0));

    // Mit Pipe — Datenfluss von links nach rechts
    var r2: f64 := -3.7 |> Abs() |> Clamp(0.0, 3.0) |> Round();

    PrintF64(r1);   // 4.0 → Clamp → 3.0 → Round → 3.0
    PrintStr("\n");
    PrintF64(r2);   // identisch
    PrintStr("\n");

    return 0;
}

Pipe mit HOF

fn Double(x: int64): int64  { return x * 2; }
fn Negate(x: int64): int64  { return -x; }
fn IsEven(x: int64): bool   { return x mod 2 = 0; }

fn main(): int64 {
    var v: int64 := 5 |> Double() |> Negate();   // -10

    if (10 |> IsEven()) {
        PrintStr("gerade\n");
    }

    return 0;
}


7. Sichtbarkeit und Modifizierer

Modifizierer Position Bedeutung
pub Vor fn Exportiert die Funktion — aus anderen Units importierbar
@inline Vor fn Compiler soll die Funktion inline expandieren
@no_opt Vor fn Keine Optimierungen — für Benchmarks, MMIO-Debugging
@extern Vor fn Externe C-Funktion ohne Implementierung in Lyx
@variadic Zusätzlich zu @extern Variadische Argumentliste im C-Stil

// Exportierte Hilfsfunktion
pub fn Clamp(val: int64, lo: int64, hi: int64): int64 {
    if (val < lo) { return lo; }
    if (val > hi) { return hi; }
    return val;
}

// Immer inlinen — minimiert Aufruf-Overhead
@inline
fn Abs(x: int64): int64 {
    if (x < 0) { return -x; }
    return x;
}

// Externe C-Funktion
@extern
fn strlen(s: pchar): int64;

// Variadisch (C printf-Schnittstelle)
@extern @variadic
fn printf(fmt: pchar): void;

→ Alle Attribute: Attribute & Pragmas · FFI: C-Interoperabilität


8. Vollständiges Beispiel: Auswertungs-Pipeline

Das folgende Beispiel kombiniert Tupel-Rückgabe, Funktionstypen, Higher-Order Functions und den Pipe-Operator in einem realistischen Muster:

import std.io;
import std.math;

// Funktionstypen
type Validator = fn(value: f64): bool;
type Transform = fn(value: f64): f64;

// Gibt (transformierterWert, erfolgreich) zurück
fn Process(value: f64,
           fn validate: Validator,
           fn transform: Transform): (f64, bool) {
    if (validate(value) = false) { return (0.0, false); }
    return (transform(value), true);
}

// Konkrete Implementierungen
fn IsPositive(v: f64): bool { return v > 0.0; }
fn IsFinite(v: f64): bool   { return v = v; }   // NaN-Prüfung: NaN ≠ NaN

fn Normalize(v: f64): f64   { return v / 100.0; }
fn Cap(v: f64): f64         { return v |> Abs() |> Clamp(0.0, 1.0); }

fn Clamp(v: f64, lo: f64, hi: f64): f64 {
    if (v < lo) { return lo; }
    if (v > hi) { return hi; }
    return v;
}

fn main(): int64 {
    var inputs: [4]f64 := [42.0, -5.0, 150.0, 0.0];
    var i: int64 := 0;

    while (i < 4) limit(4) {
        var (out, ok) := Process(inputs[i], IsPositive, Normalize);
        if (ok) {
            PrintStr("OK: "); PrintF64(out); PrintStr("\n");
        } else {
            PrintStr("UNGÜLTIG: "); PrintF64(inputs[i]); PrintStr("\n");
        }
        i := i + 1;
    }

    // Ausgabe:
    // OK: 0.42
    // UNGÜLTIG: -5.0
    // OK: 1.5
    // UNGÜLTIG: 0.0

    return 0;
}


Zusammenfassung

Konzept Syntax Beispiel
Einfache Funktion fn Name(p: T): R { return …; } fn Add(a: int64, b: int64): int64
Kein Rückgabewert Rückgabetyp weglassen fn Log(msg: pchar)
Exportiert pub fn … pub fn GetVersion(): pchar
Tupel-Rückgabe : (T1, T2) fn Divide(…): (int64, bool)
Destrukturierung var (a, b) := Fn(…) var (result, ok) := Divide(10, 3)
Anonyme Funktion fn(p: T): R { return …; } direkt als Argument
Funktionstyp-Alias type F = fn(T): R type Comparator = fn(int64, int64): int32
HOF-Parameter fn name: fn(T): R fn Apply(v: int64, fn f: fn(int64): int64)
Pipe-Operator wert |> Fn(args) -3.7 |> Abs() |> Round()
Funktion als Wert Name ohne () var f: Comparator := CompareAsc

Letzte Aktualisierung: 2026-05-22