Racket (Programmiersprache)
# Racket (Programmiersprache)

Racket ist eine multi-paradigmatische Programmiersprache aus der Lisp-Scheme-Familie. Sie ist Nachfolger des Scheme-Dialekts PLT Scheme. Racket besitzt eine integrierte Entwicklungsumgebung (IDE), DrRacket, und eine umfangreiche Bibliothek.

2018 erhielt Racket den Programming Languages Software Award von ACM SIGPLAN. In der Laudatio wurde Rackets singuläre Bedeutung sowohl für Forschung als auch Lehre von Programmiersprachen über rund zwanzig Jahre hervorgehoben. Racket ist eine Programmiersprache zur Erzeugung und Erweiterung (Language Extensibility) von Programmiersprachen. Es spielt eine wichtige Rolle in der Forschung zu rekursiven Modulen 1. Klasse, Gradual Typing, Functional Reactive Programming und Kontrakten höherer Ordnung. DrRacket fand Verbreitung in Einführungskursen zu Programmiersprachen und bei neueren Lehrbüchern.

Racket ist Open-Source-Software unter GNU Lesser General Public License bzw. MIT-Lizenz und Apache-Lizenz.

## Code-Beispiele

Das klassische Hallo-Welt-Programm:

Quadrieren einer Liste in der Basissprache racket/base (dynamische Typisierung):

Quadrieren einer Liste im Dialekt typed/racket, der statische Typisierung unterstützt:

Durch Nutzung von typed/racket zeigt DrRacket in der REPL (Read-Eval-Print-Loop, eine Art Eingabeaufforderung) Typinformationen an:

Ausschnitt einer Markdown-Vorlagen-Datei cv.md.pp für das Textsatzsystem Pollen:

In der entsprechenden Markdown-Datei cv.md steht dann

Generierung eines Sierpinski-Dreiecks:

Die grafische Ausgabe des Sierpinski-Dreiecks ist in der Abbildung rechts dargestellt.

## Sprachorientierte Programmierung

In Racket steht die Erzeugung von domänenspezifischen Sprachen oder Allzwecksprachen im Mittelpunkt.

Jede Sprache, die in Racket implementiert wird, muss zwei Bestandteile aufweisen: einen sog. Reader, der vereinfacht ausgedrückt den Quellcode einliest, und einen sog. Expander, der den Quellcode interpretiert.

Für eine Sprache minilang (für Mini-Sprache) könnte der Reader beispielsweise wie folgt aussehen:

Dieser liest den Code zeilenweise ein und erzeugt ein Syntax-Objekt (mit #`), in das die Zeilen als Strings in eine Liste in der for-Schleife eingespleißt werden (#,@), d. h. sie werden unter Eliminieren einer Listenebene eingefügt: `(list 1 2 ,@(list 3 4) 5) => (list 1 2 3 4 5). Danach werden diese der Reihe nach an das Terminal ausgegeben. Der Reader muss in jedem Fall die Funktion read-syntax zur Verfügung stellen.

Nun speichert man den Code in einer Datei minilang.rkt und legt anschließend eine Datei testminilang.rkt im selben Verzeichnis mit dem Inhalt

an. Diese kann man nun in DrRacket (oder mit racket testminilang.rkt) ausführen und es werden alle Zeilen am Terminal ausgegeben.

Hier ist zu beachten, dass der Reader nur das Syntax-Objekt zurückgibt, der Code selbst aber erst später ausgeführt wird. Wenn die Strings der Code-Zeilen modifiziert werden sollen (beispielsweise um Vorformatierungen vorzunehmen), dann kann man das Einspleißen im Syntax-Objekt verändern. Dies könnte zum Beispiel so aussehen:

Hier werden die Strings aus den Code-Zeilen mit einem Präfix „veraendere“ versehen und als Liste von Datums-Objekten gespeichert. Diese Liste wird in die Schleife innerhalb des Syntax-Objekts eingespleißt. Das bedeutet, dass jeder String mit einer Funktion veraendere aufgerufen wird. Diese wird im Expander minilang2-expander.rkt implementiert (der auch wieder im selben Verzeichnis gespeichert wird).

Führt man das testminilang.rkt von oben mit dem veränderten Reader aus, erhält man die Ausgabe:

Der gezeigte Reader erzeugt eine Terminalausgabe, wenn man die Quelldatei testminilang.rkt ausführt. Es ist aber auch möglich, mit Hilfe von require solche Module in anderen Code einzubinden. Dazu könnte man im vorliegenden Beispiel das Syntax-Objekt wie folgt modifizieren:

Hier werden die erzeugten Strings in einer Liste export-daten gespeichert und zur Verfügung gestellt. Bindet man nun das Programm testminilang.rkt wie folgt ein,

kann man auf export-daten zugreifen, die hier auf das Terminal ausgegeben werden.

In den vorangegangenen Beispielen ist eine Art Syntax und Semantik implementiert worden. Oft wird in Racket die Syntax nur leicht modifiziert (weiterhin sog. S-Expressions), aber neue Funktionalität mit Hilfe von Makros hinzugefügt. Diese dienen dazu, Syntax-Objekte ineinander zu transformieren. Hier spielen auch die verschiedenen Phasen (Phase 0 – Laufzeitphase, Phasen $>0$ – Übersetzungsphasen) in Racket eine Rolle, in die man ebenfalls eingreifen kann.

Wenn man neue Syntax (abseits von S-Expressions) einführt, ist es oft einfacher, das Einlesen im Reader in mehrere Schritte zu unterteilen. Dies sind im Einzelnen das Zerlegen des Eingabestrings in irreduzible Elemente der Sprache (Tokenizer, Lexer) und das Prüfen und Umwandeln dieser Elemente in das Syntax-Objekt entsprechend einer Grammatik (Parser). Dies wird in der zitierten Literatur ausführlich dargestellt.

