A First Look at DART, The Programming Language
2011-10-11
While browsing through the DART language specification, I thought, why not write a blog post about my first impressions. So here it is:
In the following transcript I’d like to cover the perspective as seen from a language geek. Someone who likes to talk about language features and concepts. So don’t be disappointed if you won’t find any examples here. This post is just about the details, the distilled stuff that distinguishes DART from other object-oriented programming languages.
All the following notes are based on the Draft Version 0.0.1 of the language specification and the online code samples.
What is DART?
The specification describes DART as a class-based, single-inheritance, pure object-oriented programming language, optionally typed and with reified generics and interfaces. It is C-based and so looks very similar to most languages that use ‘{‘ and ‘}’ to enclose their classes and statement scopes.
So what is so exceptional about this language?
Optionally Typed
While I prefer static typing, the burden to wrap your mind around types for every class, method and variable introduces a steep learning curve for new programmers and is often an annoying overhead for smaller projects. So DART takes the concept of a type system to a new level: Everything is dynamically typed as long types are omitted. And as soon types are specified, declarations get statically typed and verifiable. This is not only a great feature from a runtime performance perspective, but also a great tool for larger software projects that depend on clearly defined interfaces.
In practice, this means that you are free to write a program without any types defined whatsoever and specify the types as soon the project requires it.
Concurrency through Isolates (3.3)
DART is always single threaded with Erlang style actors that support concurrent threads of execution. In DART, actors are called Isolates. The name clearly stresses their most important property: There is no shared state.
Isolates are regular classes that are derived from the base class “Isolate” and are started by calling the member function spawn().
There are two different types of Isolates, “light” ones, that run in the thread that created the Isolate and “heavy” ones that run in their own thread.
When light Isolates are started from the main UI thread, they can access the “document” and “window” DOM variables.
Isolates use a ReceivePort to accept and dispatch messages. One or more SendPorts can be created from a ReceivePort. ReceivePort and SendPort represent the two ends of an asynchronous communication channel. A message that is sent to a SendPort is copied and delivered asynchronously (without blocking the sender) to the ReceivePort. A message can be sent together with another ReceivePort that is used to deliver a subsequent answer to.
This separation of receiving and sending ports combined with the option to associate another reply port for every message sent, makes asynchronous messaging simple to use. For a concise example, take a look at the IsolateSample.dart source file line 74.
Privacy (3.2)
Interestingly, the designers of the DART language chose to bake the common convention to prefix private members with an underscore (‘_’) directly into their scoping rules. Meaning that every declaration that begins with an underscore is private and not accessible from outside the current scope.
Factories (7.5.2)
DART introduces a new kind of class constructor that is called Factory. These are constructors that look like static functions with one difference in that they explicitly return an instance of the class. A factory can be called like a constructor and its method body can fully control the instance that is returned.
A factory can be useful in situations where object instances need to be recycled and returned from a cache. They can also be used when additional processing code needs to be separated from the code that initializes the class instance.
Interfaces (8)
DART supports interfaces with optional type annotations. Interfaces can contain methods, operators, getters and setters.
Optionally, an interface can specify a factory class and a number of constructors. Together, they can be used to instantiate default implementations of the interface.
Numbers (10.3)
“Dart integers are true integers, not 32 bit or 64 bit or any other fixed range representation.”
Complex Types (10.6 ff.)
DART supports Lists (denoted by []) and Maps (denoted by {}) as native types. Maps always map strings to objects.
Function Expressions (also called lambdas or closures) are first class objects. It is possible to declare functions inline and pass them around.
String Interpolation (10.5.1)
Strings can contain expressions, like “hello world, I am online since ${days} days”. If the type inserted is not a string, the toString() method is called to convert it.
Libraries (12)
Libraries is the basis of DART’s module system. Libraries seem to be comparable to a package in Java or a namespace in C#.
The Cool Stuff
I like the decision to base DART on a single threaded execution model and move the concurrency abstraction to the actor level. Admittedly, some projects may not fit to DART just because they require concurrency and shared state, but for a large number of other applications, DART may be a great language choice.
You can translate DART to JavaScript today. But the resulting code may not look like you’d expect.
Functions do support optional parameters.
Generics support F-bounded quantification. Which effectively means that the generic type parameters can be constrained. This is similar to C#, Java and Scala.
The following operators can be user-defined: ==, <, >, <=,>=, -, +, *,~/, %, j, ^, &, <<, >>, >>>, []=, [], ~, negate
There is syntactic sugar to specify property getters and setters.
Type aliases are supported through the keyword typedef.
What’s Missing
For a first version, DART is a rather complete language, but there a some features I am missing:
- There is no method to access the types from within the language yet (i.e. like Reflections in Java or C#). This is planned for a future version.
- Labels are supported, but there no goto statement (11.11).
- No nested or inner classes.
- No value types or tuples.
- No scoping statement that controls symmetric resource usage (like the using() construct in C#).
- Pattern matching. This would be a nice addition to make Isolate receivers more concise.
- No Events.
Conclusion
DART is a modern object-oriented language with a fancy new type system, it supports actor based concurrency, interfaces, optional static typing and generics. Time will tell if DART is cool enough to replace JavaScript.
Despite the fact that Isolates are a great way to abstract over concurrency and that the type system supports optional static typing, I don’t think that DART can be used effectively for larger projects. For example, the compromise to support NULLs as a valid and default value for every type could be a big source of bugs in larger projects.
From a language evolution perspective, DART is a great language. It combines static and dynamic typing quite seamlessly. Future language designers may follow up on this idea and add strong type inferencing and tool support to create programming languages that enable programmers to write dynamically typed code that can later be refined towards static types, clear interfaces and refactoring options. Such a language would be easy to learn for an entry level programmer and a great fit for larger software projects.