Guide

TorbScript in 10 Ideen

Die zehn Ideen, die TorbScript ausmachen, je ein kurzes Beispiel, für alle, die schon programmieren und die Gestalt der Sprache in fünf Minuten wollen.

TorbScript ist eine statisch typisierte Sprache für Skripte, Werkzeuge und Server, mit Wertsemantik, ohne null und ohne Exceptions. Ein Befehl, torb, führt eine Datei sofort aus und baut dasselbe Programm zu einem schnellen nativen. Das sind die zehn Ideen dahinter, jede mit einem kurzen Beispiel und einem Link auf die Station der Tour, die weitergeht.

Ziel#

Nach dieser Seite kennst du die Gestalt von TorbScript und weißt, wo die Tour, die Referenz und der Spickzettel jede Idee aufgreifen.

1. Ein zweiter Name ist eine Kopie#

const original = [1, 2]
var copy = original
copy.append 3
print "{original} {copy}"
// prints [1, 2] [1, 2, 3]

Jeder Typ ist ein Wert: Listen, Maps und deine eigenen Typen. Zwei Namen teilen sich nie einen Wert, also ändert sich ein Wert nur dort, wo du ihn änderst. Die Kopie ist billig, weil der Speicher geteilt bleibt, bis eine Seite schreibt. Siehe Bindungen.

2. Jede Änderung ist markiert#

Kompiliert nicht
const numbers = [1, 2, 3]
numbers.append 4
// error: `append` needs a `var`

Die Bindung entscheidet, ob sich ein Wert ändern kann: const nie, var schon. Ein Feld, das sich ändert, ist ein var-Feld, und eine Methode, die ihren Wert ändert, ist eine var fn. Es gibt keine MutableList: Eine var-Liste ist eine. Siehe Typen und Methoden.

3. Ein Aufruf liest sich wie ein Befehl#

print "Hello"
const count = [1, 2, 3].length()
print(count + 1)
// prints Hello
// prints 4

Ein Aufruf am Anfang einer Zeile lässt seine Klammern weg. Sie bleiben, wo sie nötig sind, und torb format schreibt sie für dich. Siehe Aufrufe ohne Klammern.

4. Ein Wert ist einer seiner Fälle, und ein match behandelt alle#

type Shape {
  case Circle(radius: Float)
  case Square(side: Float)
}

const shape = Shape.Square 2.0
match shape {
  .Circle(radius) => print(3.0 * radius * radius)
  .Square(side) => print(side * side)
}
// prints 4.0

type ist das eine Schlüsselwort für Daten. Kommt später ein Fall dazu, kompiliert jedes match, dem er fehlt, nicht mehr, und zwar an der Zeile, die einen neuen Zweig braucht. Siehe Fälle und match.

5. Kein null#

const ages = ["Ada": 36]
print(ages.get("Ada") ?? 0)
print(ages.get("Alan") ?? 0)
// prints 36
// prints 0

Ein Wert, der fehlen kann, hat einen Option-Typ, Int?, und der Compiler lässt dich den fehlenden Fall behandeln. ?? gibt den Ersatzwert, ?. greift in den Wert, wenn es einen gibt. Siehe Kein null.

6. Keine Exceptions#

match Int.tryFrom("forty-two") {
  Ok(number) => print number
  Fail(_) => print "That is not a number."
}
// prints That is not a number.

Eine Funktion, die scheitern kann, gibt ein Result zurück, und ein Fehlschlag ist eine Antwort wie jede andere. Ein ? nach einem Aufruf gibt ein Fail mit einem Zeichen an den Aufrufer zurück. Siehe Keine Exceptions.

7. Traits statt Vererbung#

trait Area {
  fn area(): Float
}

type Square with Area {
  side: Float

  fn area(): Float {
    side * side
  }
}

print Square(2.0).area()
// prints 4.0

Es gibt keine Klassen und keine Vererbung. Was ein Typ kann, ist ein Trait, gegeben mit with oder später mit extend, und ein Trait funktioniert als Typ. Siehe Traits statt Vererbung.

8. Verben ändern, Partizipien kopieren#

const names = ["Grace", "Ada"]
const more = names.appended "Alan"
print more.filter({ _.byteLength() > 3 }).map({ _.toUpperCase() }).toList()
// prints ["GRACE", "ALAN"]

append ändert ein var an Ort und Stelle, appended gibt eine geänderte Kopie zurück und funktioniert auf einem const. filter und map sind faul: Nichts läuft, bis ein letzter Schritt wie toList() die Werte durchzieht. Siehe Listen, Maps und Pipelines.

9. Deine eigenen Konstrukte sind Funktionen#

fn twice(body: () => Void) {
  body()
  body()
}

twice {
  print "hello"
}
// prints hello
// prints hello

Eine Closure als letztes Argument folgt dem Aufruf, also ist eine neue Kontrollstruktur eine gewöhnliche Funktion. Der Typ eines Parameters entscheidet, wie sein Argument gelesen wird, und das reicht auch für Konfigurationsblöcke: keine Makros, keine Annotationen. Siehe Kontrollfluss und eigene Konstrukte.

10. Warten ist ein Typ, kein Schlüsselwort#

fn double(value: Int): Task<Int> {
  spawn { value * 2 }.await()
}

print double(21).await()

Eine Funktion, die einen Task zurückgibt, darf darin await() aufrufen, so wie eine, die ein Result zurückgibt, ? benutzen darf. Ein Task läuft auf einem Pool von Workern und bekommt eine Kopie von allem, was er mitnimmt, also teilen sich zwei Tasks nie eine Variable. Siehe Tasks.

Weiter#

Esc