Tutorial 5~20 MinutenMittel

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
1

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",
};
2

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}"),
}
3

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}",
    };
}
4

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"

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}",
    };
}
5

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..10

Exklusiv: 0 bis 9

0..=10

Inklusiv: 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