Coding

Konzepte für Programmiersprachen

Wer kennt es nicht: da programmiert man ein paar Jährchen rum und plötzlich wird man von dem Größenwahn überwältigt, eine neue, eigene Programmiersprache zu entwickeln.

Im Folgenden habe ich ein paar Ideen für – mehr oder weniger – neue Konzepte für eine solche Programmiersprache aufgeschrieben, an der ich vllt. irgendwann mal arbeiten werde.

Tuples und Tuple Types

Tuples sind ein Datentyp, der quasi beliebige zusammengesetzte Daten speichern kann. Beispielsweise könnte man einen Menschen so beschreiben:

me := (name: "Paul", age: 19, gender: 'M);

Wir können also dictionary-Einträge in einem Tuple haben. Allerdings können wir auch Tuple als einfache Listen verwenden, z. B. so:

primes := (2, 3, 5, 7);

Es kommt sogar noch besser: Wir können beide Formate auch kombinieren, z. B. für folgendes:

lotto := (1, 4, 9, 16, 25, super: 36);

Häufig nutzt man in einer Anwendung viele gleichartige Tuple. Hier gibt es die Möglichkeit der Tuple Types (ein wenig ähnlich den structs in C), einem Meta-Datentyp, der leichter als eine eigene Klasse ist. Das o.g. Beispiel können wir also wie folgt formalisieren:

Human := ((String: name, Int: age, Tok: gender));

Nun könnten wir einen neuen Human erzeugen, ähnlich wie oben:

me := Human(name: "Paul", age: 19, gender: 'M);

(Hierbei nutze ich die ganze Zeit eine weitere nette Funktion: Implicit Declaration. Anstatt für jede Variable den Typ explizit anzugeben, wird diese beim ersten Mal mit dem := Operator und nicht mit dem gewöhnlichen = Operator zugewiesen, was die automatisierte Typbestimmung beinhaltet.)

Custom Operators

Okay wir kennen alle die Standardoperatoren +, -, *, /, %, **, &&, ||, sowie die Vergleichsoperatoren (ALC-Ops, Algorithmic, Logical, Comparative Operators).

Doch hier kommt die neue Idee: Custom Operators.

Mit \name expr wird der einseitige Custom Op name auf dem Ausdruck ausgeführt, was dem Funktionsaufruf (expr).op_name() entspricht.

Mit expr0 \name expr1 wird der beidseitige Custom Op name auf dem Ausdruck ausgeführt, was dem Funktionsaufruf (expr0).op_name(expr1) entspricht.

Da die Standardoperatoren auch über magische Methoden definiert wurden, kann man das auch entsprechend nutzen, also z. B. 2 \add 3 statt 2+3. Ist aber unübersichtlich und sollte in den meisten Fällen gelassen werden. ^^

At-Expressions

Der Ausdruck @expr gibt null zurück; der Ausdruck @expr0 expr1 den Wert des Ausdrucks expr1. In beiden Fällen wird der Ausdruck unmittelbar hinter dem At-Zeichen nicht ausgeführt.

Das ist nützlich für folgendes:

Expression-only Functions

Niemand macht sich gerne die Mühe, für eine einfache Funktion einen ganzen Block mit Return-Befehl zu schreiben. Dafür gibt es eine einfache Lösung: Expression-only Functions.

Damit schreibt man nicht folgendes:

fn add2 (Int: number) -> Int {
    return number + 2;
};

Stattdessen kann man folgendes schreiben:

fn add2 (Int: number) -> Int
    => number + 2;

Das ist doch schon gleich viel lesbarer. :)

Partial Application

Funktionen mit einem Mal ausführen ist nun aber auch wirklich langweilig! Daher gibt es einen Partial Application-Operator. Damit erhält man eine neue Funktion mit leicht veränderter Typensignatur, denn einige Parameter wurden bereits mit festen Argumenten besetzt.

Ein Beispiel:

Die folgenden beiden Code-Schnipsel sind vom Ergebnis her äquivalent.

add(1, 2)
add<>(Int: left, 2)(1)

Das sieht jetzt erstmal vllt. noch unspektakulär aus, aber man kann ja das Ergebnis von add<>(Int: left, 2) zwischenspeichern, an andere Funktionen übergeben u.v.m., sodass dies ein recht mächtiges Werkzeug sein dürfte.

Literal Lambdas

Sehr nützlich sind sicher auch Literal Lambdas. Wieso viel bei der Definition von Lambdas viel drum herum reden, wenn es auch einfach geht? Der Ausdruck println("hello, world") beschreibt eine Funktion, die die println Funktion aufruft.

Ausführen kann man das ganze auch ganz einfach: `println("hello, world")`()

Variablen in dem Lambda-Ausdruck werden in dem Kontext ausgewertet, indem die Lambda-Funktion ausgeführt wird. Möchte man Variablen verwenden, d.h. nicht von einer bestimmten Benennung der lokalen Variablen abhängig sein, nutzt man den Diamantenoperator von eben:

func := `2 * x + 3`<>(Int: x)
println(func(-1))