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
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ückgabewertnull– Kein Wert
Erweiterte Typen
Optional<T>– Wert oder nullResult<T, E>– Erfolg oder FehlerList<T>– Dynamische ListenMap<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),
}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> { ... }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}
}
}
";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
}interface (TypeScript-Stil) als Alternative zu Traits. Beide sind austauschbar – wähle den Stil, der dir besser gefällt.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