Skip to main content

Dart 1.22: Faster tools, assert messages, covariant overrides

Dart 1.22 is now available. It introduces a sync/async union type, assert messages, covariant parameter overrides, and much more. Tool startup is now much faster. Get it now!

Faster tool startup

We have switched to using application snapshots for running our SDK tools like dart2js, analyzer, and pub. This improves startup performance. See the AOT compiling talk at Dart Dev Summit 2016 for more information. Information about how to use application snapshots can be found in the SDK wiki.

Here are the improved performance numbers we see with the switch.


Assert messages

The fail-fast principle is crucial for building high-quality software, and assert is the simplest way to fail fast. But until now, it wasn’t possible to attach messages to asserts, so if you wanted to make your error useful, you were forced to throw a full exception. Like this:

num measureDistance(List waypoints) {
  if (waypoints.any((point) => point.isInaccessible)) {
    throw new ArgumentError('At least one waypoint is inaccessible.');
  }
  // ...
}

With messages in asserts, your code is not only shorter:

num measureDistance(List<Place> waypoints) {
  assert(waypoints.any((point) => point.isInaccessible),
         'At least one waypoint is inaccessible.');
  // ...
}

But, more importantly, asserts are completely skipped in production, so your production code will be faster (it won’t have to iterate over waypoints at the start of every measureDistance() call).

Covariant parameter override

In object-oriented class hierarchies, especially in user interface frameworks, it's fairly common to run into code like this:

class Widget {
  void addChild(Widget widget) {...}
}

class RadioButton extends Widget {
  void select() {...}
}

class RadioGroup extends Widget {
  void addChild(RadioButton button) {
    button.select();
    super.addChild(button);
  }
}

Here, a RadioGroup is a kind of widget. It refines the base Widget interface by stating that its children must be RadioButtons and cannot be any arbitrary widget. Note that the parameter type in RadioGroup.addChild() is RadioButton, which is a subclass of Widget.

This might seem innocuous at first, but it's actually statically unsound. Consider:

Widget widget = new RadioGroup(); // Upcast to Widget.
widget.addChild(new Widget());    // Add the wrong kind of child.

Tightening a parameter type, that is, using a proper subtype of the existing one in an overriding definition, breaks the Liskov substitution principle. A RadioGroup doesn't support everything that its superclass Widget does. Widget claims you can add any kind of widget to it as a child, but RadioGroup requires it to be a RadioButton.

Breaking substitutability is a little dubious, but in practice it works out fine. Developers can be careful and ensure that they only add the right kinds of children to their RadioGroups. However, because this isn't statically safe, many languages disallow it, including Dart strong mode. (Classic Dart permits it.)

Instead, users must currently manually tighten the type in the body of the method:

class RadioGroup extends Widget {
  void addChild(Widget widget) {
    var button = widget as RadioButton;
    button.select();
    super.addChild(button);
  }
}

The declaration is now statically safe, since it takes the same type as the superclass method. The call to select() is safe because it's guarded by an explicit as cast. That cast is checked and will fail at runtime if the passed widget isn't actually a RadioButton.

In most languages, this pattern is what you have to do. It has (at least) two problems. First, it's verbose. Many users intuitively expect to be able to define subclasses that refine the contracts of their superclasses, even though it's not strictly safe to do so. When they instead have to apply the above pattern, they are surprised, and find the resulting code ugly.

The other problem is that this pattern leads to a worse static typing user experience. Because the cast is now hidden inside the body of the method, a user of RadioGroup can no longer see the tightened type requirement at the API level.

Both problems are solved by covariant parameter overrides. To enable them on a method parameter, you mark it with the contextual keyword covariant:

class Widget {
  void addChild(covariant Widget widget) {...}
}

Doing so says "A subclass may override this parameter with a tighter desired type". A subclass can then override it like so:

class RadioGroup extends Widget {
  void addChild(RadioButton button) {
    // ...
  }
}

No special marker is needed in the overriding definition. The presence of covariant in the superclass is enough. The parameter type in the base class method becomes the original type of the overridden parameter. The parameter type in the derived method is the desired type.

This approach fits well when a developer provides a library or framework where some parameter types were designed for getting tightened. For instance, the Widget hierarchy was designed like that.

In cases where the supertype authors did not foresee this need, it is still possible to tighten a parameter type by putting the covariant modifier on the overriding parameter declaration.

The covariant modifier can also be used on mutable fields. Doing so corresponds to marking the parameter in the implicitly generated setter for that field as covariant:

class Widget {
  covariant Widget child;
}

This is syntactic sugar for:

class Widget {
  Widget _child;
  Widget get child => _child;
  set child(covariant Widget value) { _child = value; }
}

Learn more about this feature in the changelog or in the informal spec.

The Null type is now a subtype of every other type

In other words, the Null type has been moved to the bottom of the type hierarchy. The null literal was always treated as a bottom type. Now the named class Null is too:

final empty = <Null>[];

String concatenate(List<String> parts) => parts.join();
int sum(List<int> numbers) => numbers.fold(0, (sum, n) => sum + n);

concatenate(empty); // OK.
sum(empty); // OK.

FutureOr<T>

A lot of asynchronous code in Dart allows the use of a T or a Future<T>. For example, the callback to Future.then is declared to take a T but it doesn't specify the return type, since it could be an S or a Future<S>:

Future<S> then<S>(onValue(T value), { Function onError });

We are adding FutureOr<T> to support this use case. FutureOr<T> represents the union of Future<T> and T. So the signature of then now looks like this.

Future<S> then<S>(FutureOr<S> onValue(T value), { Function onError });

This tightens types in places where dynamic was used before (like with `then` above), and allows strong-mode tools to do better type inference. In non-strong-mode, FutureOr just means dynamic. In other places, where types were already tight but the implementation had to force asynchronous code, it will allow to loosen that requirement.

Generalized tear-offs are going away

Generalized tear-offs are no longer supported, and will cause errors. We updated the language spec and added warnings in 1.21, and are now taking the last step to fully de-support them. They were previously only supported in the VM, and there are almost no known uses of them in the wild.

Use of Function as a class is now deprecated

You can still use Function as a type name, but don’t use it as a class (don’t extend it, implement it, etc.). For example:

// This is deprecated.
class MyFunction extends Function {
  // …
}

But this is still okay

// This is okay.
void myAwesomeMethod(Function callback) {
  // …
}

Read the changelog for more information and additional changes.

This release took one additional week to finish (our regular schedule is a release each 6 weeks) but we think it was worth the wait.

Popular posts from this blog

Dart in 2016: The fastest growing programming language at Google, 2nd fastest growing in TIOBE Index

Dart was the fastest growing programming language at Google in 2016 with millions of lines of code written. It also made it to TIOBE Index Top 20 this month (see TIOBE's methodology ). It takes time to build something as ambitious as Dart and, in some ways, Dart is still in its infancy. But we're glad the hard work is starting to pay off. Many thanks to our amazing community! We're going to celebrate by ... releasing 1.22 next week (as per our usual 6 week release schedule).
Read more

AngularDart is going all Dart

Until now, the multiple language flavors of Angular 2 were written as TypeScript source, and then automatically compiled to both JavaScript and Dart. We're happy to announce that we’re splitting the Angular 2 codebase into two flavors – a Dart version and a TypeScript/JavaScript version – and creating a dedicated AngularDart team. This is amazing news for Dart developers because: The framework will feel more like idiomatic Dart. It will make use of Dart features that couldn't work with the TypeScript flavor. It will be faster. This is equally great news for our TypeScript and JavaScript developers, by the way. Cleaner API, performance gains, easier contributions. Read more on the Angular blog. Angular 2 for Dart is used by many teams at Google. Most famously by the AdWords team, but many other Google teams build large, mobile-friendly web apps. Some of the top requests from these teams were: make the API feel like Dart, provide a faster edit-refresh cycle, and
Read more

The new AdWords UI uses Dart — we asked why

Google just announced a re-designed AdWords experience. In case you’re not familiar with AdWords: businesses use it to advertise on google.com and partner websites. Advertising makes up majority of Google’s revenue, so when Google decides to completely redo the customer-facing front end to it, it’s a big deal. The Dart team is proud to say that this new front end is built with Dart and Angular 2. Whenever you asked us whether Google is ‘even using Dart for anything,’ this is what we had in mind but couldn’t say aloud. Until now. We asked Joshy Joseph , the primary technical lead on the project, some questions. Joshy is focusing on things like infrastructure, application latency and development velocity, so he’s the right person to ask about Dart.   Q: What exactly did we launch on Monday? It’s a complete redesign of the AdWords customer experience that is rolling out slowly as a test to a small initial set of advertisers. The most noticeable thing is probably the Material
Read more