First Impressions of Dart

A Reaction Post of Sorts

August 26, 2019

I spent some time looking at Dart, speciafially because I want to try out Flutter at some point. However I did not feel like starting with Dart and Flutter so I went straight for just Dart so get a feel for the language before I dive into Flutter and all that comes with mobile app development.

My initial impressions of Dart are good. Dart looks like a langauge intended to have as few surprises as possible. The syntax is familiar straight forward and extremely familiar to anyone who has ever programmed in Javascript or any other C-like derivitie.

Dart is strongly typed, with support for type inference, and can compoile to mobile, web, and native targets. This makes Dart very easy to get started with.

Most of the syntax is pretty boring, and even their FAQ suggests that that’s the way they inteded it to be. However, boring does not mean entirely uninteresting. The rest of the post I will be talking about the couple things that happened to catch my eye.

Cascade Notation

The first bit of syntax is refered to in the Dart documentation as cascade notation. Cascade notation allows a programmer to invoke a sequence of methods on a common object. This is similar to commonly seen pattern in some languages called a fluent-interface. While not entirely the same, it is similar.

Cascade notation looks something like this…

myButton
  ..text = "Get Dart"
  ..backgroundColor = 0xAA0000
  ..show();

It really is just syntactic-sugar, however it has the benefit of allowing programmers to use something like a fluent-interface without the need to implement certain seemingly inconfruent implementation details into their objects, such as returning this on any set-method, and allows updating member field on objects.

Generators

Dart has support for generators, howver what’s special about dart is that generators come in two flavors: sync and async. The difference is that sync generators return an Iterable and async generators return a Stream.

Iterables contain the values directly, while Streams are contain future’s of a value.

Sync Generators

Sync generators look like this…

Iterable<int> getEvens(int n) sync* {
  int k = 0;
  while (k < n) {
    yield 2 * k++;
  }
}

getEvens return an Iterable<int> of n even numbers. This means we can iterate through the list with a for loop simply like:

for (int num in getEvens(10)) {
  print("Got: $num");
}

Now in itself is not an uncommon feature, however I include it to contrast with async generators.

Async Generators

Async generators look like this…

Stream<int> getEvents(int n) async* {
  int k = 0;
  while (k < n) {
    yield 2 * k++;
  }
}

Note that the only difference is the sync* to async* and Iterable<int> to Stream<int>.

The big difference comes around when it’s time to consume the generator.

Dart allows us to consume streams with a special await for construct. So we can build a similar program to the sync consumer.

await for (int num in getEvens(10)) {
  print("Got: $num");
}

This async for-loop is pretty neat, as it makes it simpler to wait for the fulfilment of the next item in the stream. (*Considerations for using async-for)

Typedefs

The last Dart feature which a bit limited as of the time of writing, but very useful is the ability to define type aliases for functions via typedef.

This allow us to reduce noise within our method definitions. For example without typedef’s we could have the following…

class FilterCollection {
  Function predicate;

  FilterCollection(bool f(Object a)) {
    predicate = f;
  }
}

FilterCollection is defining a constructor that accepts f, a function that takes an object and returns a bool.

With a typedef we can simplify FilterCollection and more effectively write…

typedef Predicate = bool Function(object a);

class FilterCollection {
  Predicate predicate;
  FilterCollection(this.predicate);
}

Typedefs really start to shine when you have functions that accept multiple other functions, or you want to provide more meaning into how a function is used. At this time, it looks like typedefs can only be used to alias function definitions, but my hope is that this extends to other types int he futuure. Using types to clarify intent through something other than variable/function names is a valuable thing to have.

Conclusion

In this post I’ve covered some of the more interesting things I’ve found about Dart. The langugage is very familiar, and very easy to get productive in. The things that stand out to me as a first-time Dart user are: cascade notation, async/sync generators, and typedefs. The documentation for Dart is top-notch, and the language is very approachable. I look forward to exploring Flutter in the future for mobile development.

Further Reading