Donnerstag, 23. April 2009

Programmierung: Ein kleiner Einblick in D

Jeder der sich schon mal mit C++ befasst hat kommt irgendwann mal an den Punkt an dem er denkt "Oh man geht das nicht auch einfacher?". Fakt ist: Kaum eine Sprache ist so schwer zu lernen und so aufwändig durch den Compiler zu kriegen wie diese, dazu kommen noch einige C Altlasten und vor allem der Preprozessor. Oft ertappt man sich dabei möglichst trickreich die Sprache aus den Angeln zu hebeln (mir fällt da spontan das sizeof Voodoo ein um zur Compilezeit herauszufinden ob ein Cast gemacht werden kann oder nicht). Viele dieser Tricks basieren auf ausgefuchsten C++-Templates und gerade die sorgen bei vielen Programmierern für spontanes "schreiend im Kreis rennen" (schon mal mit Typelists gearbeitet? :P). Hier springt D von Digitalmars in die Bresche, oder versucht es zumindest. Ein paar Highlights möchte ich hier beleuchten.

Keine Rückwärtskompatibilität

Was in C++ Anfangs noch Mittel zum Zweck war ist inzwischen zu einem unangenehmen Anhängsel geworden. Vielen geht schon beim Anblick eines const char* die Sause geschweige denn der ganze Mist den Arrays mit sich schleppen. Vieles davon bessert die STL schon lange nach (std::string und std::vector sei dank) ein wenig übel ist der Nachgeschmack dennoch... zumindest für mich ;). Besonders "schön" ist da vor allem der Preprozessor hat er doch keine Ahnung was es mit der Sprache auf sich hat und setzt nur fröhlich irgendwelche Markos oder Definitionen ein. Entscheidungen die zur Compilezeit getroffen werden gehen an im vorbei da er ja schon lange fertig ist wenn es zur Sache geht. Hier kommen dann bei C++ die Templates zum Schuss and genau dort wird es für viele kompliziert. D bietet hier ein eigenes Sprachmittel: static if.

import std.stdio;

class A {}
class B : A {}
class C {}

void main(string[] args) {
static if (is(B : A)) {
writefln("B erbt von A");
} else {
writefln("B erbt nicht von A");
}
static if (is(C : A)) {
writefln("C erbt von A");
} else {
writefln("C erbt nicht von A");
}
}

Ohne große Umwege lässt sich hier das Gewünschte ausdrücken. Ein Preprozessor würde hier versagen da er keine Ahnung von Sprachmitteln wie Vererbung hat, man wäre also auf C++-Template Voodoo angewiesen, hier ist schon alles in der Sprache drin.

Besonders Templates wurden in D stark überarbeitet, da gerade hier sich Schwächen auftuen die zu sehr großen Problemen führen können. Das ganze Elend fängt schon bei der Syntax an. Da die Spitzen Klammern die in C++ verwendet werden auch irrtümlicherweise als größer oder kleiner-Operatoren angesehen werden können (in solchen Situationen muss man dem Compiler meist noch unter die Arme greifen). D benutzt hier eine etwas gewöhnungsbedürftige Syntax die allerdings diese Probleme umgeht da sie einen Operator verwendet der im Normalfall nur unär ist hier aber binär verwendet wird, nämlich den !-Operator.

import std.stdio;

template Foo(T) {
T a = 666;
}

void main(string[] args) {
alias Foo!(int) tmp;
writefln("%d",tmp.a);
}

Hier taucht auch gleichzeitig eine andere Besonderheit von D auf, das alias. Dieses Schlüsselwort macht eigentlich nichts anderes als das was wir von C/C++ kennen, denn im Gegensatz zu diesen Sprachen verfügt D über echte Typedefs die auch überladen können.

import std.stdio;

typedef int bla;

void test(int a) {
writefln("int: %d",a);
}

void test(bla a) {
writefln("bla: %d",a);
}

void main(string[] args) {
int a = 666;
bla b = 777;
test(a);
test(b);
}

Eine andere Kleinigkeit die bei unserem Template Beispiel aufgefallen ist, ist das es sich bei diesem Template weder um eine Template-Klasse noch um eine Template-Funktion handelt. Es ist einfach nur ein Template. Das bietet eine weitere Interessante Möglichkeit, nämlich Mixins.

import std.stdio;

template Foo(T) {
void test() {
writefln("Foo.test");
}
}

class Test {
public {
mixin Foo!(int);
}
}

void main(string[] args) {
Test t = new Test;
t.test();
}

Ein nicht ganz so offensichtliches Feature ist hier der Garbage Collector und der Fakt das hier nicht mit Pointern (keine Sorge die gibt es auch noch) sondern mit Referenzen gearbeitet wird wenn eine Klasse instanziiert wird. Hier scheiden sich sicherlich die Geister denn was bei Systemprogrammierung durchaus Sinn macht, kann beim Programmieren von Applikationen schnell zur Katastrophe werden und in wirklich scheusslichen Speicherlecks enden (oder pro-aggressiven Einsätzen von auto_ptr, shared_ptr und Konsorten).

Was auch sehr viel Spass macht ist das Wegfallen von Forward-Declarations. So geht z.B. folgendes:

import std.stdio;

void foo() {
bar();
}

void bar() {
writefln("foobar");
}

void main(string[] args) {
foo();
}


Kritikpunkte

D ist eine tolle Sprache und räumt mit einigen Sachen auf die bei C++ zu Verwirrung oder schlechtem Programmierstil führen auf. Dennoch ist die Unterstützung der Sprache noch mehr als dürftig da es im Endeffekt nur sehr wenige Compiler für sie gibt die im Grunde alle auf dem DMD von Digitalmars basieren. Auch fehlen mir einige In-Depth Einblicke die in für C++ zu Hauf gibt und gerade auf der Low-Level Seite von mir schmerzlich vermisst werden (bei Nachfrage wird man dann auch gerne mit der Standardantwort "read to code" abgefrühstückt). Auch ist die eine Aufspaltung der D Community durch 2 unterschiedliche "Standard"-Bibliotheken mehr als offensichtlich und auch nervig (Flamewars gibt es wirklich schon genug). Während ein Compiler Phobos nutzt kommt der andere nur mit Tango daher. Derzeit wird auch an D2.0 herumgeschustert und man kriegt langsam das Gefühl als werde auch diese Sprache immer überladener. Dennoch ist die Sprache so entworfen das einem nicht gleich pfundweise Fehlermeldungen um die Ohren geschleudert werden da gerade auf obskure veraltete Syntax verzichtet wurde. Für alle die aber etwas gesucht haben das so schnell wie C/C++ ist und mit viel vom Java/C# Komfort daherkommt ist diese Sprache sicherlich mal einen Blick wert. Java? Ja, D verzichtet unter anderem auf Multivererbung und setzt dafür auf Interfaces. Auch Schlüsselwörter wie final, abstract und synchronized sind oft einfach hilfreich wenn es darum geht schnell und leserlich das auszudrücken was man meint.

Das hier sind nur ein paar sehr kleine Details von dem was D ausmacht und bei weitem noch nicht alle. Sollte Interesse bestehen kann ich gerne auch noch etwas auf Details eingehen (vor allem Closures dürften dein ein oder anderen sicher interessieren, das sprengt hier aber glaub ich etwas den Rahmen ^^).

Kommentare:

blueyed hat gesagt…

Interessant. Habe selber noch nichts damit gemacht, aber bin vor einiger Zeit darauf gestoßen, weil es wohl jemanden gibt, der sehr viele nette Spiele damit umsetzt.
IIRC ein Japaner - aber ich finde das leider auf die Schnelle nicht wieder.
Bei einem der Spiele raste man durch einen sich ständig windenden Tunnel/Röhre (gitternetzartig), und die Unmenge an verschiedenen Spielen hat auf mich den Eindruck hinterlassen, dass es damit relativ schnell geht, so etwas umzusetzen.
Kennst Du die Seite / den Programmierer?

xRaich[o]²x hat gesagt…

Könnte sein das das einer ist der in #D rumhängt. Da sind einige die Spiele coden, aber wo die genau lokalisiert sind weiß ich nicht.