====== Lyx – Generics & Interfaces ====== Lyx kennt zwei Formen der Abstraktion über Typen: **generische Funktionen**, die für jeden eingesetzten Typ eigenen Code erzeugen (Monomorphisierung), und **Interfaces**, die eine gemeinsame Schnittstelle für verschiedene Klassen festlegen. > **''trait'' und ''impl'' gibt es nicht.** Frühere Fassungen dieser Seite beschrieben eine Rust-artige Syntax (''trait Printable { … }'', ''impl Printable for P { … }''). Beides wird mit ''unexpected top-level token'' abgewiesen und steht auch nicht in der Grammatik. Die Schnittstelle heißt ''interface'', die Zuordnung ''implements'' — beschrieben in Abschnitt 2. → [[lyx_-_programmiersprache:sprache:oop|OOP – Klassen und Vererbung]] · [[lyx_-_programmiersprache:sprache:funktionen|Funktionen]] · [[lyx_-_programmiersprache:sprache:datentypen|Datentypen]] ---- ===== 1. Generische Funktionen ===== Ein Typparameter steht in spitzen Klammern hinter dem Funktionsnamen. Der Compiler erzeugt für jeden tatsächlich verwendeten Typ eine eigene Fassung — der Aufruf ist damit ein gewöhnlicher, direkter Sprung ohne Laufzeit-Auflösung. import std.io; fn Id(x: T): T { return x; } fn Pair(a: A, b: B): A { return a; } fn main(): int64 { PrintLn(IntToStr(Id(42))); // 42 PrintLn(IntToStr(Pair(7, 9))); // 7 return 0; } Der Typ lässt sich auch ausdrücklich angeben, etwa für Structs: type P = struct { x: int64; }; fn main(): int64 { var p: P; p.x := 5; var q: P := Id

(p); PrintLn(IntToStr(q.x)); // 5 return 0; } Ein Typparameter darf innerhalb der Funktion an einen weiteren generischen Aufruf weitergereicht werden: fn Zweimal(x: T): T { return Id(x); } * Ein Typparameter gilt nur **innerhalb** seiner Funktion. * Mehrere Parameter werden mit Komma getrennt: ''fn Pair<A, B>''. * Die eckige Schreibweise ''%%Id[T]%%'' gibt es nicht — der Parser erwartet spitze Klammern. > **Generische Typen gibt es nicht.** ''type Box<T> = struct { … }'' und die Klassenvariante werden mit ''unexpected top-level token'' abgewiesen. Generisch sind allein **Funktionen**. Für einen typunabhängigen Container bleibt der Weg über ''int64''-Adressen (→ [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]]). ---- ===== 2. Interfaces ===== Ein ''interface'' listet Methodensignaturen — jede endet mit einem Semikolon, ohne Rumpf. Eine Klasse verpflichtet sich mit ''implements'' darauf. import std.io; type Zeichenbar = interface { fn Flaeche(): int64; }; type Kreis = class implements Zeichenbar { r: int64; fn Flaeche(): int64 { return self.r * self.r; } }; fn main(): int64 { var k: Kreis := new Kreis(); k.r := 4; PrintLn(IntToStr(k.Flaeche())); // 16 return 0; } **Die Verpflichtung wird geprüft:** Fehlt eine Methode, meldet der Compiler ''class missing interface method''. Das ist der eigentliche Gewinn — die Schnittstelle ist nicht bloß Dokumentation. ==== Als Parametertyp ==== Der Interface-Name taugt als Typ. Damit lässt sich eine Funktion schreiben, die jede implementierende Klasse annimmt: unit main; import std.io; import std.string; type Zeichenbar = interface { fn Flaeche(): int64; }; type Kreis = class implements Zeichenbar { r: int64; fn Flaeche(): int64 { return self.r * self.r; } }; type Quadrat = class implements Zeichenbar { s: int64; fn Flaeche(): int64 { return self.s * self.s; } }; fn Zeige(z: Zeichenbar): int64 { return z.Flaeche(); } fn main(): int64 { var k: Kreis := new Kreis(); k.r := 3; var q: Quadrat := new Quadrat(); q.s := 5; PrintLn(IntToStr(Zeige(k))); // 9 PrintLn(IntToStr(Zeige(q))); // 25 return 0; } 9 25 Der Aufruf über den Interface-Typ trifft die richtige Implementierung — das war bis lyxc 1.0.14 nicht der Fall ([[https://github.com/SEOLizer/LyX-Compiler/issues/1133|Issue #1133]]). ==== Mehrere Interfaces ==== type Lesbar = interface { fn Lies(): int64; }; type Schreibbar = interface { fn Schreib(v: int64): int64; }; type Datei = class implements Lesbar, Schreibbar { wert: int64; fn Lies(): int64 { return self.wert; } fn Schreib(v: int64): int64 { self.wert := v; return 0; } }; ==== Was Interfaces nicht können ==== ^ Konstrukt ^ Verhalten ^ | ''%%type S = struct implements I%%'' | abgewiesen — nur Klassen können ''implements'' tragen | | ''%%type B = interface extends A%%'' | abgewiesen — keine Interface-Vererbung | | Methodenrumpf im Interface | die Klasse muss die Methode dennoch selbst mitbringen; eine Standardimplementierung entsteht daraus nicht | ---- ===== 3. Typbedingungen ===== > **Schranken an Typparametern haben keine Wirkung.** Die Grammatik führt in ''TypeParamList'' nur Bezeichner — eine Form ''fn Max<T: Vergleichbar>(…)'' ist dort nicht vorgesehen. Der Parser nimmt sie zwar an, aber ein Methodenaufruf auf dem Typparameter scheitert mit ''undefined function''. > > Wer eine Schnittstelle erzwingen will, nimmt den **Interface-Typ als Parametertyp** (Abschnitt 2) — das prüft der Compiler zuverlässig. ---- ===== 4. Monomorphisierung ===== Für jeden verwendeten Typ entsteht eine eigene Fassung der Funktion. Das hat zwei Folgen: * **Kein Laufzeit-Overhead.** Der Aufruf ist ein direkter Sprung; es gibt keine Typprüfung zur Laufzeit und keine Indirektion über eine Tabelle. * **Code wächst mit der Zahl der Typen.** Eine generische Funktion, die mit fünf Typen aufgerufen wird, liegt fünfmal im Binary. Für sicherheitskritischen Code ist das die erwünschte Eigenschaft: Die WCET einer monomorphisierten Funktion ist exakt berechenbar, weil der Zieltyp zur Übersetzungszeit feststeht. ---- ===== 5. Statischer und dynamischer Dispatch ===== ^ Weg ^ Auflösung ^ WCET ^ Einsatz ^ | Generische Funktion | Übersetzungszeit (Monomorphisierung) | exakt | Algorithmen über mehrere Typen | | Interface als Parametertyp | Laufzeit | eingeschränkt | Austauschbare Komponenten, Treiber | | Virtuelle Methode (''virtual''/''override'') | Laufzeit (V-Table) | eingeschränkt | Klassenhierarchien → [[lyx_-_programmiersprache:sprache:oop|OOP]] | In ''@flight_crit''-Funktionen ([[lyx_-_programmiersprache:sprache:attributes-pragmas|Attributes & Pragmas]]) ist der generische Weg vorzuziehen: Er erzeugt keinen indirekten Sprung, dessen Ziel erst zur Laufzeit feststeht. ---- ===== 6. Generics im Safety-Umfeld ===== ^ Situation ^ Empfehlung ^ | DAL-A/B-Regelzyklus | Generische Funktion — monomorphisiert, WCET exakt | | Treiber, austauschbare Komponenten | Interface als Parametertyp | | Container über mehrere Typen | Es gibt keine generischen Typen; feste Typen oder ''int64''-Adressen | | Schnittstelle erzwingen | ''implements'' — der Compiler prüft die Vollständigkeit | ---- **Weiterführende Seiten:** * [[lyx_-_programmiersprache:sprache:oop|OOP — Klassen, Vererbung, virtuelle Methoden]] * [[lyx_-_programmiersprache:sprache:funktionen|Funktionen — Signaturen und Rückgaben]] * [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher — typunabhängige Container]] Letzte Aktualisierung: 2026-08-09 Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).