My first few thoughts about record, pattern and class modifier

Here are some thoughts after reading the documentation, I am not intend to be original and any examples are dedicated to their original authors.

Records

Reference doc: Records | Dart

The documentation may be somewhat insufficient to explain the type annotation, I add more examples here:

(int, int, {int a, int b}) record1 = (2, a: 1, b: 2, 1);
(int, int, {int a, int b}) record2 = (1, 2, b: 2, a: 1);
(int, int, {int a, int b}) record3 = (1, b: 2, 2, a: 1);

print(record1 == record2); // false
print(record2 == record3); // true
  • When you define your record, the order is not matter (but you still need to maintain the general order of positional fields), the compiler can match the data to the corresponding field once you define your record correctly.
  • Say the second line, (int, int, {int a, int b}) record2 = (1, 2, b: 2, a: 1), 1 is matched to the FIRST positional field, 2 is matched to the SECOND positional field, 2 is matched to named field b, 1 is matched to the named field a.

Pedagogically, I think the doc is somewhat too early in introducing how to access a record’s field. I think explaining how the type annotation works first. Then, it is easier to understand why accessing field with the following syntax:

var record = ('first', a: 2, b: true, 'last');

print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'

It is clearer why need to use $1 and $2 to access the positional field instead of using $1 and $4 after you understand the inner working of type annotation.

I think records should be introduced in book1 near the end of the book, probably after introducing maps. And introducing the application of returning multiple values of a function immediately. Earlier chapters in function can make a note to refer to here.

Another application is parallelization of futures of different types, which can introduce in book2 of Future chapters.

Patterns

Official doc: Patterns | Dart

The documentation is long but should be clear to the reader. I just want to mention some syntax difference when between direct assignment and matching:
(Just copy from my own notes)

variable assignment

var record1 = (3, "two");

var (a, b) = record1; // correct
(var c, var d) = record1; // wrong

  
// wrong below
if (record1 case var (e, f)) {
  // do something
}
// correct below
if (record1 case (var g, var h)) {
  // do something
}

Explanation:

  • For case pattern matching, the assignment keyword must go inside, while outside when using direct variable assignment. (Why?)

rest (...) and wildcard (_)

var [a, b, ..., c, d] = [1, 2, 3, 4, 5, 6, 7];
// Prints "1 2 6 7".
print('$a $b $c $d');

var [e, f, _, _, _, g, h] = [1, 2, 3, 4, 5, 6, 7];
print('$e $f $g $h');

var (a, b, ..., c, d) = (1, 2, 3, 4, 5, 6, 7);
// error
print('$a $b $c $d');

var (e, f, _, _, _, g, h) = (1, 2, 3, 4, 5, 6, 7);
print('$e $f $g $h');

Explanation:

  • rest keyword acts as a placeholder for several elements and can be used in list only
  • wildcard acts as a placeholder for single element only and can be used in records, maps, list etc…
  • wildcard has another usage to use as default in switch statement.

In my opinion, general concept of pattern can be introduced in beginning chapter of book 2 (may be after string manipulation chapter). Then, reinforcing the concepts sparingly on later chapter. For example, introduce destructure data about record, then introduce destructure class instance data in the later class chapter.

There is one application of writing algebraic data type. It combines the exhaustiveness checking of switch and sealed class feature, this can be mentioned in a separate chapter after sealed class, may be named “switch expression” and mention this application altogether. If it is just too short, just add the guard clause of switch here.

By the way, the official doc of pattern has told us WHEN to use algebraic data type, but not WHY we need to use it. But the official doc of switch: Branches | Dart has told you WHY but not WHEN to use it.

class modifier

Official doc: Class modifiers | Dart

There is one important change in mixin: Dart 3.0 no longer allows classes to be used as mixins by default. Instead, you must explicitly opt-in to that behavior by declaring a mixin class:

Other than that, the doc is really clear.
I think base, interface and final modifier can be introduced together:

extended implemented
base Yes No
interface No Yes
final No No

Explanation:

  • The restriction is applied to outside of the library only. Within the same file, it just doesn’t matter.

Then, introduce sealed and some switch examples in another chapter.

The official has explained the drawbacks of extends and implements here: Class modifiers for API maintainers | Dart

It gives an example for implements and worth to see. (Adapt this example a little bit, it illustrate how to access a private method in another library.)

The example of extends here (adapt from wiki directly):
In library a.dart, we define:

class Super {

  var _counter = 0;

  void inc1() {
    _counter++;
    print(_counter);
  }

  void inc2() {
    _counter++;
    print(_counter);
  }
}

In library b.dart, we define:

class Sub extends Super {
  @override
  void inc2() {
    inc1();
  }
}

In main of b.dart, we run:

var sub = Sub();
sub.inc2(); // 1 will be printed

It seems don’t have problem, however, if we change the implementation of inc1 of the parent class as follows:

void inc1() {
  inc2();
}

an instance of Sub will cause an infinite recursion between itself and the method inc1() of the super-class and eventually cause a stack overflow.

Just re-write some chapters in book2 to incorporate the class modifier content.

Combining class modifier

I think the official doc hasn’t mention enough examples about this. I hope the author can give more examples about combining class modifier.

2 Likes

Thank you for your summary and suggestions! As you may have already discovered, teaching through writing is a good way to learn yourself. It also helps other people. Your audience here in the forums is going to be fairly small. You might consider writing on Medium or answering Stack Overflow questions. Or even applying to write for Kodeco. You’d have my recommendation.

I don’t know the answer either to your why question about var going inside and outside of the parentheses.

The more I learn about class modifiers, the more I think they don’t really belong in either book. Not a full teaching anyway. A partial teaching using sealed and abstract interface would work well, but most of the class modifiers are geared for package authors, and neither of the books teaches how to write packages. What we need is an advanced Dart book which can go into more depth about topics like this.

2 Likes

Thank you for your response and suggestion. @suragch I agree with you that the class modifier may not be so necessary for beginner.

The why word is written in my own notes and I forget to delete. :grinning: I just make a note here so that later may be I can find an answer. I don’t expect any answer about this actually. Thank you for your careful catching.

I will consider posting in medium if I do have something very important to share. Now, these are immature thoughts and I just post these to hope that it would be a little bit helpful on your preparation of second (or third?) edition.

2 Likes