Dart: The Good Parts

Not only am I a programmer, but I have a general interest in both spoken and programming languages. I like the way they make me think about expressing ideas. So every now and then I continue my quixotic search for "The Perfect Programming Language".

Go

A while ago, that led me to learn a bit of Go, which was pretty enlightening. Here was a language that offered compile-time type safety but wasn't verbose like Java. And instead of relying on class hierarchies like many languages, it relied only on interfaces. But it had a couple of problems in my eyes.

First, its types are often implicit instead of explicit. If you implement some method Foo(), that might implicitly make you implement some Fooer interface, even though your contract for Foo() may differ from that of the interface. The contract, in my opinion, is just as important as a method's paramater and return types, so I prefer explicitly marking what interfaces my classes implement.

Secondly, though Go seems fond of its implicit types, it still allows programmers to define types in terms of structs, which completely locks down an implementation to that single type. For a function Foo(bar Bar), if Bar is a struct, you never have the option of passing in your own implementation of Bar.

Dart

A few weeks ago, I took some time to learn Dart, another programming language brought to you by Google. Initially, I was prepared to hate it. Especially after my (albeit limited) experience with GWT, "compiles to JavaScript" was not exactly an enticing feature. Thankfully, I continued on to explore a bit of the core language, because it solves the above problems.

The first problem is easy enough to solve -- don't have implicit types. You have to explicitly declare that a class in Dart extends another or implements an interface. Easy enough -- this is the default for most languages.

But Dart also solves the second problem -- a problem that once drove me to research making my own language. In Dart, you can declare that a class implements any other class! Thus, to reuse the example above, if a function foo(Bar bar) exists, I can make my own class and declare that it extends Bar, then pass it in anywhere that a Bar is expected. I love this! It really stops programmers from accidentally putting up road blocks to reusability.

And, because any class can be used as a de facto interface, you don't have to bother with the boilerplate of writing separate interfaces. When prototyping, you can just make a well documented default implementation of a class. Later, you can come in and backfill your implementations with more complete versions without having to refactor everything. You can even use Dart's factory constructors so that API consumers get your new implementation without needing to change a line of code. Awesome!

Other good stuff

Dart also has a lot of other nice features. It's got mixins, which makes adding common functionality to your classes super easy. In particular the IterableMixin declares an abstract iterator, which you must implement. But once you do, it provides you with implementations for all of the Iterable interface automatically.

Let's see, there's also named parameters -- you can declare that some or all of your method's parameters are named, which makes for much more readable APIs. For example, consider:

doThing(myObject, 10);

vs.:

doThing(myObject, repeat: 10);

And, as you would expect from a modern scripting language, Dart has full object encapsulation. Object "fields" are really just transparent getters/setters. Dart has a nice syntax for defining your getters and setters, or it can define default ones for you for publicly accessible fields. So you can go from:

class User
{
    String name;
}

to:

class User
{
    String _name; /* (private) */

    set name(String newName)
    {
        if (newName == null) throw new ArgumentError("User.name may not be null.");
        _name = newName;
    }

    String get name => _name;
}

... without any of your API users needing to change a line of code.

The other parts

These features make me wish Dart were suited to become the language that I use for all my new projects, but a couple things hold it back. Well, I guess they all boil down to one big thing: Dart is designed to compile to JavaScript and run in the browser.

One side-effect of this is that Dart, like JavaScript, is inherently single-threaded. You can't easily fire up a new thread to do some work in the background. JavaScript's (and thus Dart's) solution to this is to allow you to do asynchronous programming by popping callbacks onto a queue that will get executed later by an event loop. That's an OK hack to allow a browser UI to update itself, but not really how I prefer to program.

"Ah," you Dart programmers are probably saying, "you can just use an isolate to execute things in parallel!" Well, yes. But the Isolate API is a bit cumbersome to use. For example, if I want two-way communication between an isolate, I have to pass in a SendPort, have my isolate construct a ReceivePort locally, send its corresponding SendPort back through the SendPort the isolate received on startup, and make sure to read it and handle it on the consuming end. Oh, and if either end forgets to close its ReceivePort, the Dart VM will wait forever for them to close before exiting.

"Well, that's fine.", I thought to myself. "I mean, how often do I need to write multi-threaded applications?" Not that often, really. Most of the quick utilities that I write in Python don't require multithreading, just a list of tasks to run in order. But the Dart libraries force you to use event-loopy callbacks even if you don't want them. For example:

...
var fileContents;
var future = myFile.readAsString();
future.then( (readData) {
    fileContents = readData;
});

// BZZT!  This does not work.  Your Future object hasn't executed yet!
// Because you need to read a file, *all* subsequent procedural code 
// must happen within the context of callbacks. Bleh.
doSuff(with: fileContents);

Dart could solve this by adding a method to Future that allows you to execute it inline and wait for the result, instead of forcing you into an event loop when you don't need one.

Keeping an Eye Out

I'd say that for its stated purpose, being "a platform for scalable web app engineering", Dart has… erm… hit the mark. ;) If I need to make a client-side web app, I'll definitely look into doing it in Dart instead of JavaScript.

I only complain about the above issues because I like the syntax and other features of Dart so much. It's only because I want to use it for everything that I care that it doesn't quite seem the best tool for that purpose yet. Maybe in future releases of Dart my concerns will be addressed. Here's hoping!

comments powered by Disqus