Saturday, December 29, 2012

I Did Not Understand Constant Constructors

‹prev | My Chain | next›

If you really want to understand Dart constant constructors, read the comments to last night's post on the subject. Lasse wrote an excellent, succinct introduction to the subject. As for me, I learn best by playing with code, so tonight I follow up on yesterday's attempt at understanding.

The main thing that I was doing wrong yesterday was in instantiating my constant objects. I had been using the usual new keyword:
class PerfectCookie {
  final number_of_chips;
  const PerfectCookie({this.number_of_chips});
}

main() {
  var perfect_cookie = new PerfectCookie(number_of_chips: 42);
}
It turns out that const is overloaded to not only declare constant values, but it can also instantiate constant objects:
main() {
  var perfect_cookie = const PerfectCookie(number_of_chips: 42);
}
Following through with Lasse's notes, I expect that multiple instances of constant objects are the same thing—that is they should be identical:
main() {
  var pc1 = const PerfectCookie(number_of_chips: 42),
      pc2 = const PerfectCookie(number_of_chips: 42);

  print("pc1 and pc2 identical? ${identical(pc1, pc2)}");
}
Running this code results in:
pc1 and pc2 identical? true
As pointed out to me, constant constructors create compile-time constants. Another way of saying this, and this is what I missed yesterday, is that all of the associated values are known (and fixed) at compile time.

At the risk of repeating Lasse's entire comment, Dart used to allow only these compile time constants to be assigned to instance variables at compile-time. I never really understood what those compile-time constants were back then and, now that instance variables are lazily evaluated, they don't matter as much. Ah well, that's progress.

I still find it strange that using the regular new keyword works for constant constructors:
class PerfectCookie {
  final number_of_chips;
  const PerfectCookie({this.number_of_chips});
}

main() {
  var pc1 = const PerfectCookie(number_of_chips: 42),
      pc2 = const PerfectCookie(number_of_chips: 42),
      pc3 = new PerfectCookie(number_of_chips: 42);

  print("pc1 and pc2 identical? ${identical(pc1, pc2)}");
  print("pc1 and pc3 identical? ${identical(pc1, pc3)}");
}
The first and second "perfect cookies" are identical because their values are set at compile-time. The third perfect cookie is not evaluated until runtime. Even though the value for the number of chips is the same for all three objects, the Dart compiler cannot guarantee that the number of chips is fixed, hence the third instance refers to a different object than the first two:
pc1 and pc2 identical? true
pc1 and pc3 identical? false
I can seen the suggested benefits of using constant constructors as enums and sentinels. I may have to explore annotations because I was not even aware of this feature of Dart.

New to me is that Dart's case statement requires compile-time constants. So I might write the following:
main() {
  var pc1 = const PerfectCookie(number_of_chips: 42),
      pc2 = const PerfectCookie(number_of_chips: 42),
      pc3 = new PerfectCookie(number_of_chips: 42);

  print("pc1 and pc2 identical? ${identical(pc1, pc2)}");
  print("pc1 and pc3 identical? ${identical(pc1, pc3)}");

  const pc42 = const PerfectCookie(number_of_chips: 42),
      pc84 = const PerfectCookie(number_of_chips: 84),
      pc99 = const PerfectCookie(number_of_chips: 99);

  var cookie = pc1;
  switch(cookie) {
  case(pc42):
    print(42);
    break;
  case(pc84):
    print(84);
    break;
  case(pc99):
    print(99);
    break;
  }
}
Which would result in printing the value of 42 since the pc1 and pc42 are the same:
pc1 and pc2 identical? true
pc1 and pc3 identical? false
42
Constant constructors still seem of limited use in typical Dart coding, but I feel much better for having (mostly) explored this corner of the language.


Day #614

No comments:

Post a Comment