====== 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: [[lyx_-_programmiersprache:erste-schritte|Erste Schritte]] · Syntax: [[lyx_-_programmiersprache:syntax|Sprachsyntax]] · Methoden: [[lyx_-_programmiersprache:oop|OOP & Klassen]] · Generics: [[lyx_-_programmiersprache:generics-traits|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''); ''return'' ohne Wert oder ganz weglassen.
* ''pub'' macht 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(value: In, fn transform: fn(In): Out): Out {
return transform(value);
}
fn main(): int64 {
var doubled: int64 := MapApply(21, fn(x: int64): int64 {
return x * 2;
});
PrintInt(doubled); // 42
PrintStr("\n");
return 0;
}
→ Ausführliche Generics-Syntax: [[lyx_-_programmiersprache:generics-traits|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: [[lyx_-_programmiersprache:threads-nebenlaeufikeit|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: [[lyx_-_programmiersprache:attributes-pragmas|Attribute & Pragmas]] · FFI: [[lyx_-_programmiersprache: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