Monday, March 19, 2012

Obscure Dart Constructors

‹prev | My Chain | next›

I have myself a nice, Dart modal dialog. It is an adapter for Element with the following constructors:
class ModalDialog implements Element {
  Element el, bg;

  factory ModalDialog() => new ModalDialog.tag('div');

  ModalDialog.tag(String tag) {
    el = new Element.tag(tag);
  }

  ModalDialog.html(String html) {
    el = new Element.tag('div');
    el.innerHTML = html;
  }
  // ...
}
The factory constructor allows me to instantiate a modal dialog thusly:
el = new ModalDialog();
el.innerHTML = '<form>...</form>';
That works, but the factory constructor is more ceremony than Dart requires. In fact, I can use a redirecting constructor, as Kasper Lund pointed out. Thus I can still instantiate simple modal dialogs the same way if I change the constructor to:
class ModalDialog implements Element {
  Element el, bg;

  ModalDialog(): this.tag('div');
  // ...
}
That is some compact syntax—the vanilla ModalDialog() constructor redirects to the ModalDialog.tag() constructor with an argument of 'div'. I really like that.

Something else that Kasper mentioned was that redirection also works in constant constructors. Upon hearing that, I could only wonder what the heck is a constant constructor? To answer that, I return to my Player class:
class Player {
  int x, y;

  Player.xy(this.x, this.y) {
    if (x == null) x = 0;
    if (y == null) y = 0;
  }
}
I can instantiate a player as new Player.xy(25, 25); and the player is positioned at x=25 and y=25. The thing about most Dart classes is that you cannot simply assign them outside of functions:
// this won't work...
var me = new Player.xy(25, 25);

main() {
  // do stuff here ...
}
The problem is that new Player.xy(25, 25) is not a compile time constant. I can declare the me variable in a Dart libraries scope, but I cannot assign it—unless the assignment is a constant expression. Actual assignment is deferred until runtime (e.g. inside the main() function).

To convert Player into a compile time constant, I need to make all instance variables final:
class Player {
  final int x, y;
  // ...
}
Admittedly, there is not much sense to a game in which all player positions are final, but why let that get in the way of a good topic exploration?

In addition to final instance variables, I also need a constant constructor. Here, I use a redirecting, constant constructor to point the vanilla Player() to the Player.xy() version:
class Player {
  final int x, y;

  const Player(): this.xy(0,0);

  Player.xy([this.x, this.y]) {
    if (x == null) x = 0;
    if (y == null) y = 0;
  }
}
With that, I can now instantiate a constant object at compile time:
final me = const Player();

main() {
  // Not much to do here since players don't move
}
Clearly I need a better use-case for constant objects that an immovable Player if I am to include a treatment of this topic in Dart for Hipsters. And I do need to include it—these redirecting constructors are quite nice.


Day #330

No comments:

Post a Comment