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;
}
- Rückgabetyp steht nach dem Doppelpunkt hinter der Parameterliste.
- Kein Rückgabewert: Rückgabetyp weglassen (implizit
void);returnohne Wert oder ganz weglassen. pubmacht eine Funktion aus anderen Units importierbar.
// 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
