Ein Jahr JavaScript-Konkurrent Dart

Sprachen  –  2 Kommentare

Es ist etwa ein Jahr her, dass Google mit Dart eine zweite Programmiersprache vorgestellt hatte. Seitdem haben sich sowohl das Team um Gilad Bracha und Lars Bak als auch die Community um die Weiterentwicklung der Sprache, der Bibliotheken und der Werkzeuge gekümmert. Mittlerweile ist man auf einem guten Weg zum ersten Meilenstein.

Anfang des Jahres stellte die iX die Sprache Dart, die ihre Wurzeln in JavaScript nicht verleugnen kann, erstmals vor [1]. Seinerzeit war das Fazit, dass noch einige Zeit bis zur Reife der Sprache benötigt werde. Mittlerweile haben ihre Entwickler diverse Ecken und Kanten abgeschliffen und fehlende Konzepte hinzugefügt. Damit hat sich Dart mehr von JavaScript emanzipiert und fühlt sich nun reifer an. Hierzu passen der Ausbau der Bibliotheken und deren streckenweise Reorganisation sowie die Ergänzung des sich in Eclipse integrierenden Editors um zahlreiche hilfreiche Features.

Eine Sache des Ausdrucks

Im Großen und Ganzen hat sich Dart wenig verändert, der Kern ist in wichtigen Konstrukten stabil geblieben. Wichtiger sind hingegen die kleineren Korrekturen sowohl in den Features als auch im Komfort. Eine der Änderungen betrifft das Typsystem. Es betrachtet Typen selbst als Objekte, sie lassen sich über die Getter-Methode type ermitteln. Ebenso können Entwickler die Namen von Typen direkt in Ausdrücken verwenden. Das ermöglicht einfache Vergleiche wie myObject == MyType. Die Definition von Gettern wie dem obigen wurde in der Notation ebenfalls vereinfacht, sodass sich auf die nicht benötigten Klammern verzichten lässt. Es genügt ein einfaches

num get answer => 40 + 2; 

zur Definition eines Getters, für den entsprechende Setter lautet die Notation passenderweise

set answer(num value) => ...;

Eine weitere Vereinfachung ist der Verzicht auf die explizite Definition von Interfaces. Vielmehr leitet sich durch die Definition einer konkreten oder abstrakten Klasse automatisch ein gleichnamiges Interface ab. Es sei also eine Klasse Duck wie im Folgenden definiert:

class Duck {
final String name;
Duck(this.name);

quack(other) => print("Hi, ${other}, I'm ${this.name}.");
}

In dem Fall kann eine Klasse Frog das Interface nutzen, ohne gleich die ganze Klasse zu erben.

class Frog implements Duck { 
quack(other) => print("I'm a quacking frog, ${other}.");
}

Aus Sicht der Go-Programmierung ist die Entscheidung nicht glücklich. Der Ansatz dort ist genau entgegengesetzt. Mit der Definition eines Interfaces beschreibt man die an eine Instanz gestellte Erwartungshaltung. Für die Typen ist dabei nicht explizit anzugeben, ob sie ein Interface erfüllen. Das ergibt sich implizit aus den implementierten Methoden. So sagt

type Quackable interface { 
Quack(p Pitch)
}

aus, dass etwas in einer Tonhöhe quaken kann. Ein eigener Typ, beispielsweise Duck, kann das implementieren.

type Duck struct { ... } 

func (d *Duck) Quack(p Pitch) { ... }

Man beachte, dass kein explizites implements benötigt wird. Deshalb funktioniert ein Konzert der Form

func Concert(q Quackable) { ... } 

mit Enten und Fröschen. Erfüllt ein Typ nicht das von ihm geforderte Interface, reklamiert der Compiler es bei der Übersetzung. Diese Denkweise scheint dem Autor für Schnittstellen natürlicher. Dafür haben die Entwickler eine andere Schwäche bezüglich abstrakter Klassen getilgt. Sie lassen sich nun im Gegensatz zu älteren Versionen nicht mehr instanziieren. Das wird, neben der Warnung im Editor, ab sofort zur Laufzeit entdeckt, in dessen Folge ein AbstractClassInstantiationError geworfen wird.