Pattern Matching
Meistere das mächtigste Feature für Kontrollfluss in Velisch – sicherer und ausdrucksstärker als jedes switch-Statement.
Was du lernen wirst
- Grundlegende match-Syntax und Wildcards
- Destructuring von Structs und Enums
- Guards für zusätzliche Bedingungen
- Praktische Anwendungsfälle
Grundlagen
Das match Statement in Velisch ist weit mächtiger als ein klassisches switch. Es erzwingt Vollständigkeit (du musst alle Fälle behandeln) und ermöglicht komplexes Destructuring.
Einfaches Matching
let status = "pending";
let message = match (status) {
"pending" => "Wird bearbeitet...",
"success" => "Erfolgreich!",
"error" => "Fehlgeschlagen",
_ => "Unbekannter Status", // Wildcard für alle anderen
};
print(message); // → "Wird bearbeitet..."Mehrere Werte mit OR
Mit | kannst du mehrere Patterns kombinieren:
let day = "Samstag";
let type = match (day) {
"Montag" | "Dienstag" | "Mittwoch" | "Donnerstag" | "Freitag" => "Werktag",
"Samstag" | "Sonntag" => "Wochenende",
_ => "Ungültiger Tag",
};
// Auch mit Zahlen
let grade = match (score) {
90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 => "A",
80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 => "B",
_ => "C oder schlechter",
};Enums matchen
Die wahre Stärke von Pattern Matching zeigt sich bei Enums – besonders bei solchen mit Daten.
Einfache Enums
enum Color { Red, Green, Blue }
fn getHex(color: Color): string {
return match (color) {
Color.Red => "#FF0000",
Color.Green => "#00FF00",
Color.Blue => "#0000FF",
};
// Kein _ nötig – alle Fälle sind abgedeckt!
}Enums mit Daten (Tagged Unions)
Enums können Daten tragen – perfekt für State-Machines und Ergebnistypen:
enum Status {
Idle,
Loading(progress: number), // Mit einem Wert
Success(data: string),
Error(code: number, msg: string), // Mit mehreren Werten
}
fn handleStatus(status: Status): string {
return match (status) {
Status.Idle => "Bereit",
// Wert extrahieren und verwenden
Status.Loading(p) => "Laden: {p}%",
// Daten auspacken
Status.Success(data) => "Ergebnis: {data}",
// Mehrere Werte
Status.Error(code, msg) => "Fehler {code}: {msg}",
};
}
// Verwendung
let s1 = Status.Loading(45);
print(handleStatus(s1)); // → "Laden: 45%"
let s2 = Status.Error(404, "Nicht gefunden");
print(handleStatus(s2)); // → "Fehler 404: Nicht gefunden"💡 Result & Optional
Die eingebauten Typen Result undOptional sind Enums – Pattern Matching ist der sauberste Weg, mit ihnen umzugehen:
let result: Result<User, string> = fetchUser(id);
match (result) {
Ok(user) => print("User: {user.name}"),
Err(msg) => print("Fehler: {msg}"),
}Guards (Zusatzbedingungen)
Mit if nach einem Pattern kannst du zusätzliche Bedingungen hinzufügen. Das nennt man Guards.
Guards mit Werten
fn getGrade(score: number): string {
return match (score) {
s if s >= 90 => "A",
s if s >= 80 => "B",
s if s >= 70 => "C",
s if s >= 60 => "D",
_ => "F",
};
}
// Praktisches Beispiel: Preisstufen
fn getDiscount(age: number, isMember: boolean): number {
return match (age) {
a if a < 12 => 50, // Kinder: 50%
a if a >= 65 => 30, // Senioren: 30%
_ if isMember => 15, // Mitglieder: 15%
_ => 0, // Sonst: 0%
};
}Guards mit Enum-Daten
enum Response {
Success(code: number, data: string),
Error(code: number, msg: string),
}
fn handleResponse(resp: Response): string {
return match (resp) {
// Nur Erfolg mit Code 200
Response.Success(code, data) if code == 200 => "OK: {data}",
// Andere Erfolgscodes
Response.Success(code, data) => "Unerwarteter Code {code}: {data}",
// Client-Fehler (4xx)
Response.Error(code, msg) if code >= 400 && code < 500 =>
"Client-Fehler: {msg}",
// Server-Fehler (5xx)
Response.Error(code, msg) if code >= 500 =>
"Server-Fehler: {msg}",
// Andere Fehler
Response.Error(_, msg) => "Fehler: {msg}",
};
}Struct Destructuring
Du kannst Structs direkt im Pattern auseinandernehmen und auf einzelne Felder prüfen.
Structs matchen
struct User {
name: string,
role: string,
age: number,
}
fn describeUser(user: User): string {
return match (user) {
// Exakter Feldwert
User { role: "admin", .. } => "Administrator",
// Feld extrahieren und Guard
User { name, age, .. } if age < 18 =>
"{name} ist minderjährig",
// Mehrere Felder prüfen
User { name: "System", role: "bot", .. } =>
"System-Bot",
// Feld extrahieren
User { name, role, .. } =>
"{name} ({role})",
};
}
let admin = User { name: "Anna", role: "admin", age: 30 };
print(describeUser(admin)); // → "Administrator"
let kid = User { name: "Tim", role: "user", age: 12 };
print(describeUser(kid)); // → "Tim ist minderjährig".. ignoriert alle nicht genannten Felder. Ohne es müsstest du jeden Feldwert explizit angeben – selbst wenn du ihn nicht brauchst.Verschachteltes Destructuring
struct Address {
city: string,
country: string,
}
struct Customer {
name: string,
address: Address,
}
fn getShippingZone(customer: Customer): string {
return match (customer) {
// Verschachtelt matchen
Customer { address: Address { country: "DE", .. }, .. } =>
"Deutschland",
Customer { address: Address { country: "AT" | "CH", .. }, .. } =>
"DACH-Region",
Customer { address: Address { country, .. }, .. } =>
"International: {country}",
};
}Range-Patterns
Für Zahlenbereiche kannst du elegante Range-Patterns verwenden:
fn getAgeGroup(age: number): string {
return match (age) {
0..=2 => "Baby",
3..=12 => "Kind",
13..=19 => "Teenager",
20..=64 => "Erwachsener",
65..=120 => "Senior",
_ => "Ungültiges Alter",
};
}
// Auch für Zeichen
fn charType(c: char): string {
return match (c) {
'a'..='z' => "Kleinbuchstabe",
'A'..='Z' => "Großbuchstabe",
'0'..='9' => "Ziffer",
_ => "Sonderzeichen",
};
}0..10Exklusiv: 0 bis 9
0..=10Inklusiv: 0 bis 10
Zusammenfassung
Was du gelernt hast:
- match-Syntax mit Wildcards und OR-Patterns
- Enums mit Daten matchen und extrahieren
- Guards für zusätzliche Bedingungen
- Struct-Destructuring und Ranges
Nächste Schritte:
- Die Standard Library erkunden
- REST APIs mit Decorators bauen