A month ago I spoke at the Dart Developer Summit about object models with source_gen (video). I mentioned some packages and techniques that are highly deserving of a closer look, so here I am to tell the full story.

First up: built_collection.

The built_collection package provides SDK-like collections with immutability. Immutablity for:

Simplicity, convenience. An immutable collection can be passed around without worrying about who might modify it.

Performance. Mutability leads to expensive patterns like defensive copying and change detection.

The fact that built_collection is supposed to be convenient poses some challenges for the API. Let’s take a look. First, an immutable list:

var list = new BuiltList<int>([1, 2, 3]);

Now I’d like to add a value to it. Of course, it’s immutable, so I can’t; what I actually want is to create a new list with an additional value tacked onto the end.

There is where we encounter the builder pattern for which the package is named. You get a new list by converting to a builder, making modifications, then building:

var builder = list.toBuilder();

builder.add(4);

var newList = builder.build();

Builders are usually used inline, so let’s do that:

var newList = (list.toBuilder()..add(4)).build()

This is as far as the builder pattern usually goes, but Dart has lambdas, so we can do better:

var newList = list.rebuild((b) => b.add(4));

What we find is that method cascading in Dart makes the builder pattern very powerful. You can almost always do what you want inline:

var newList = list.rebuild((b) => b

..add(4)

..addAll([7, 6, 5])

..sort()

..remove(1));

So, as hoped — built_collection is convenient.