Tutorial 2~20 MinutenAnfänger

Syntax & Typsystem

Tauche tiefer in die Velisch-Syntax ein – von Decorators über String-Interpolation bis hin zu Traits und Generics.

Was du lernen wirst

  • Das Typsystem verstehen (Primitive, Optional, Result)
  • Decorators für APIs und Security nutzen
  • String-Interpolation meistern
  • Traits und Generics anwenden
1

Das Typsystem

Velisch ist statisch typisiert mit starker Typinferenz. Das bedeutet: Der Compiler kennt zur Kompilierzeit alle Typen, aber du musst sie nicht immer explizit angeben. Das Beste aus beiden Welten!

Primitive Typen

  • string – Text ("Hello")
  • number – Zahlen (42, 3.14)
  • boolean – Wahrheitswerte (true/false)
  • void – Kein Rückgabewert
  • null – Kein Wert

Erweiterte Typen

  • Optional<T> – Wert oder null
  • Result<T, E> – Erfolg oder Fehler
  • List<T> – Dynamische Listen
  • Map<K, V> – Schlüssel-Wert-Paare

Optional – Sichere Null-Behandlung

Statt null-Checks überall zu verteilen, macht Optional<T>explizit, dass ein Wert fehlen kann:

// Funktion kann null zurückgeben
fn findUser(id: string): Optional<User> {
    let user = db.find(User, id);
    if (user == null) {
        return Optional.none();
    }
    return Optional.some(user);
}

// Aufruf – du MUSST den None-Fall behandeln
let result = findUser("123");
match (result) {
    Some(user) => print("Gefunden: " + user.name),
    None => print("User nicht gefunden"),
}

Result – Fehlerbehandlung ohne Exceptions

Result<T, E> zwingt dich, Fehlerfälle zu behandeln:

fn divide(a: number, b: number): Result<number, string> {
    if (b == 0) {
        return Result.err("Division durch null!");
    }
    return Result.ok(a / b);
}

let result = divide(10, 0);
match (result) {
    Ok(value) => print("Ergebnis: " + value),
    Err(msg) => print("Fehler: " + msg),
}
2

Decorators

Decorators sind spezielle Annotationen mit @, die Funktionen mit zusätzlichen Fähigkeiten ausstatten. Sie sind das Herzstück von Velischs API-First-Ansatz.

HTTP-Decorators registrieren Funktionen automatisch als API-Endpunkte:

// GET – Daten abrufen
@GET("/api/users")
fn getUsers(): List<User> {
    return db.findAll(User);
}

// POST – Neue Ressource erstellen
@POST("/api/users")
fn createUser(name: string, email: string): User {
    return db.save(User { name, email });
}

// PUT – Ressource komplett aktualisieren
@PUT("/api/users/:id")
fn updateUser(id: string, name: string): User {
    let user = db.find(User, id);
    user.name = name;
    return db.save(user);
}

// DELETE – Ressource löschen
@DELETE("/api/users/:id")
fn deleteUser(id: string): void {
    db.delete(User, id);
}

💡 Decorator-Stacking

Du kannst mehrere Decorators kombinieren! Sie werden von oben nach unten ausgeführt:

@Auth                      // 1. Auth-Check
@Role("admin")             // 2. Rollen-Check
@RateLimit(10, "1m")       // 3. Rate-Limiting
@Cache(ttl: "30s")         // 4. Caching
@GET("/api/admin/users")   // 5. HTTP-Route
fn getAdminUsers(): List<User> { ... }
3

String-Interpolation

Velisch macht String-Zusammensetzung elegant. Mit geschweiften Klammern {}kannst du Variablen und Ausdrücke direkt in Strings einbetten.

Grundlagen

let name = "Anna";
let age = 28;

// Einfache Variablen einbetten
let greeting = "Hallo, {name}!";
// → "Hallo, Anna!"

// Mehrere Variablen
let intro = "{name} ist {age} Jahre alt.";
// → "Anna ist 28 Jahre alt."

Ausdrücke & Funktionsaufrufe

Du kannst beliebige Ausdrücke in die Klammern setzen:

let x = 10;
let y = 20;

// Mathematische Ausdrücke
let result = "Summe: {x + y}";    // → "Summe: 30"
let calc = "{x} × {y} = {x * y}"; // → "10 × 20 = 200"

// Funktionsaufrufe
fn getFullName(first: string, last: string): string {
    return "{first} {last}";
}

let name = "Name: {getFullName("Anna", "Schmidt")}";
// → "Name: Anna Schmidt"

// Bedingte Ausdrücke
let status = "Status: {if (x > 5) "groß" else "klein"}";
// → "Status: groß"

Multi-Line Strings

Perfekt für SQL-Queries, JSON-Templates oder längere Texte:

let userId = "user_123";
let minAge = 18;

// SQL Query mit Interpolation
let sql = "
    SELECT * FROM users
    WHERE id = {userId}
    AND age >= {minAge}
    ORDER BY created_at DESC
";

// JSON Template
let json = "
{
    "user": "{userId}",
    "timestamp": "{now()}",
    "filters": {
        "minAge": {minAge}
    }
}
";
4

Traits & Generics

Traits definieren gemeinsame Verhaltensweisen, die verschiedene Typen implementieren können. Zusammen mit Generics ermöglichen sie hochflexiblen, wiederverwendbaren Code.

Traits definieren

Ein Trait ist wie ein "Vertrag" – jeder Typ, der ihn implementiert, muss die definierten Methoden bereitstellen:

// Trait definieren
trait Serialize {
    fn toJson(): string;
    fn fromJson(json: string): Self;
}

// Trait implementieren
struct User {
    id: string,
    name: string,
    email: string,
}

impl Serialize for User {
    fn toJson(): string {
        return "{
            "id": "{self.id}",
            "name": "{self.name}",
            "email": "{self.email}"
        }";
    }
    
    fn fromJson(json: string): User {
        let data = Json.parse(json);
        return User {
            id: data.id,
            name: data.name,
            email: data.email,
        };
    }
}

Generics mit Trait-Constraints

Generics erlauben es, Funktionen zu schreiben, die mit verschiedenen Typen arbeiten:

// Generic Funktion – T muss Serialize implementieren
fn serializeAll<T: Serialize>(items: List<T>): List<string> {
    return items.map((item) => item.toJson());
}

// Verwendung
let users = [
    User { id: "1", name: "Anna", email: "anna@test.com" },
    User { id: "2", name: "Max", email: "max@test.com" },
];

let jsonList = serializeAll(users);
// → ["{"id":"1",...}", "{"id":"2",...}"]

// Mehrere Constraints
fn processAndLog<T: Serialize + Debug>(item: T): void {
    print(item.debug());      // Debug-Trait
    save(item.toJson());      // Serialize-Trait
}

Zusammenfassung

Was du gelernt hast:

  • Primitive und erweiterte Typen (Optional, Result)
  • Decorators für HTTP, Security und mehr
  • Elegante String-Interpolation mit {}
  • Traits und Generics für wiederverwendbaren Code

Nächste Schritte:

  • Arbeite mit Collections (Listen, Maps)
  • Lerne funktionale Programmierung mit Closures
  • Meistere Pattern Matching