Monday, December 3, 2012

Improving Class Definitions with Lazily Evaluated Instance Variables

‹prev | My Chain | next›

One of the bigger challenges maintaining a book like Dart for Hipsters is keeping the code up-to-date with all that is going on in Dart. This is not simply a matter of being a good citizen by maintaining packages like dart dirty and Hipster MVC. I also have to go back through the old branches in my sample application to ensure that they are up-to-date. This is especially challenging since the application is supposed to have evolved from branch to branch. I do not think it feasible to work through the entire process each time I update the book, so instead I settle for keeping each branch working and passing analysis.

The first branch mentioned in the book is your_first_dart_app. Let's see what shape it is in...

It turns out that there was not too much that needed fixing. I replaced the plus operator on strings with concat, replaced XMLHttpRequest with HttpRequest, and updated optional parameters—all for the most recent changes to Dart. After running it through dart_analyzer, everything is good to go.

I move into the mvc and do the same. There are numerous getters and setters in this branch that need updating. I also have to update for the new library syntax, but I have been through all of that before.

Eventually, I do find something new to change. Thanks to one of the errata for Dart for Hipsters, I know that I can clean up some of the code initialization for classes. Specifically, instance variables can be lazily defined. So instead of:
class HipsterModel {
  var attributes, on;

  HipsterModel(this.attributes) {
    on = new ModelEvents();
  // ...
I can now define that on instance variable where it is first declared, which means that the constructor no longer even needs a body:
class HipsterModel {
  var attributes;
  var on = new ModelEvents();

  // ...
In the early days of Dart, the assignment would have required a compile-time constant. This is a big win.

The win is even bigger in the HipsterCollection class, which used to be:
class HipsterCollection implements Collection<HipsterModel> {
  var on;
  List<HipsterModel> models;

  HipsterCollection() {
    on = new CollectionEvents();
    models = <HipsterModel>[];
  // ...
Since I can define both of those instance variable lazily, I can eliminate the constructor entirely:
class HipsterCollection implements Collection<HipsterModel> {
  var on = new CollectionEvents();
  List<HipsterModel> models = [];
  // ...
In the absence of an explicit constructor, Dart will forward instantiation onto the superclass's constructor (Object in this case), which is exactly what I want.

I have the feeling that I need to implement this in the Hipster MVC package, but this is enough progress for one night. Another fine Dart win.

Day #588


  1. Note that instance variables (fields) are _not_ lazily initialized. Their initialization actually runs as a part of constructor (you can think about it as a sugar for initializer list). That's why you can't refer to 'this' in there.

    Also note that top-level and static variables _are_ initialized lazily (on first access). It has (should have) no semantic effect, but it lets Dart run the main function as soon as possible.

    1. Ahhh, thanks for the clarification. That makes a lot of sense.