Dart Language and Library Newsletter

2017-07-28 @floitschG

Welcome to the Dart Language and Library newsletter.

Introduction

In this, hopefully, regular newsletter, I will discuss topics the language/library team spends time on. Some of the selected subjects are still under discussion and few decisions presented in this document are final. On the one hand this means that readers can't assume that the information is accurate, on the other hand it allows me to give better insights and talk about interesting subjects much earlier than if I waited for final decisions.

This newsletters is written from my personal perspective. Especially for actively discussed topics, not all team-members always agree, and you might find different opinions on the mailing lists. I will try to keep these conflicts to a minimum, but it is unlikely that I will be able to remove all personal bias.

If You Missed It

Dart 1.24 has been released some time ago. It included some nice changes to the languages. Here is a small description of the more important ones, in case you missed them.

Function Types

We added a new way to write function types:

typedef F = void Function();

int foo(int Function(int) f) => f(499);

The following text is a copy of the 1.24 Changelog entry:

Intuitively, the type of a function can be constructed by textually replacing the function's name with Function in its declaration. For instance, the type of void foo() {} would be void Function(). The new syntax may be used wherever a type can be written. It is thus now possible to declare fields containing functions without needing to write typedefs: void Function() x;.

The new function type has one restriction: it may not contain the old-style function-type syntax for its parameters. The following is thus illegal: void Function(int f()).

typedefs have been updated to support this new syntax. Examples:

  typedef F = void Function();  // F is the name for a `void` callback.
  int Function(int) f;  // A field `f` that contains an int->int function.

  class A<T> {
    // The parameter `callback` is a function that takes a `T` and returns
    // `void`.
    void forEach(void Function(T) callback);
  }

  // The new function type supports generic arguments.
  typedef Invoker = T Function<T>(T Function() callback);

While a possibility, we haven‘t yet decided if we want to deprecate the old syntax. On the other end of the spectrum, we haven’t yet decided if we want to allow the old-style syntax within the Function syntax. (Feedback welcome).

We have plans to change the semantics of old-style function types when there is only one identifier for the argument. For example, typedef F(int); is currently a typedef for a function that takes dynamic. The new syntax gets this right: typedef F = Function(int); correctly makes this a typedef for a function that takes int.

We can't make this change (from argument-name to type-name) in one go. Instead, we want to deprecate and disallow the one-identifier syntax (making typedef F(int) illegal) first, and then, at a later point, change the behavior to read it as a type.

Here is an informal spec for the function syntax change: https://gist.github.com/eernstg/ffc7bd281974e9018f10f0cb6cfee4aa

Void Changes

Dart 1.24 came with two changes on how we handle void.

  1. void arrow functions may now have non-void expressions on the right-hand side. This is purely for convenience. For example, users can now write:
  void foo() => x++;
  void set bar(v) => _bar = v;

We still need to update the style guide (which is currently recommending curly braces for void functions).

  1. in checked mode, void does not require the value to be null anymore. This was a prerequisite for (1), but was necessary for independent reasons. Dart allows void functions to be subclassed with non-void functions, and it was thus hard to guarantee that void always became null.
class A {
  void foo() { ... }
}
class B extends A {
  int foo() { ... }
}

// In the function, `a` could be a `B` and thus return an `int` from the
// `foo` call. With the old behavior the function would then throw in checked mode.
void bar(A a) => a.foo();

Now this is not a problem anymore.

Progress

The Dart team is currently doing big refactorings to migrate towards a unified front-end. In the long run this reduces code duplication and provides a more unified experience. For example, the errors and warnings of all our tools will be the same. At the same time, it will make it easier to do language changes that require front-end support (which is the case for almost every language change).

This means that it is currently a bad timing to do language changes: not only are we spending resources on front-ends that will eventually disappear, but most tools are currently in refactoring mode, where things change rapidly and conflicting merges are common. For these reasons, the team focuses on areas that are either high priority, or don't need (lots of) front-end changes.

Under Active Development

This section summarizes areas we are actively working on.

Better Organization

The Dart team produces lots of documents (proposals, ...). So far, we didn't do a good job in collecting them and organizing them. It was not always clear which document was the most recent or agreed upon. We want to do a better job there.

As a first step, we will focus our attention on docs/language in the SDK repo. Every feature we agreed on, should have a document there. Since updating the actual spec is very time consuming, we use an informal folder for specifications that aren't yet integrated into the specification.

We will also be more aggressive in writing separate specs of features that aren‘t planned for immediate implementation, or store documents that aren’t completely finished, yet.

Resolved part-of

Resolved “part of”s have been implemented. (https://dartbug.com/20792)

In future versions of the Dart SDK it will be possible to use a URI to refer back from a part to its library:

part of "myLibrary.dart";

Make Zones strong-mode clean.

A lot of work went into making Zones strong-mode clean. This work was gated on having generic function types (and a syntax for it). With the new function-type syntax, this was made possible. For example, the RunHandler typedef is now:

typedef RunHandler =
    R Function<R>(Zone self, ZoneDelegate parent, Zone zone, R Function() f);

Unfortunately, this is a breaking change, and some code needs to be updated. We have patches for most of the packages that are affected (often in a separate branch as for the pool package: https://github.com/dart-lang/pool/tree/zone.strong). If your code uses zones (especially creating new ZoneSpecifications), feel free to reach out to me to prepare for the upcoming change.

Void as a Type.

We want to allow void as a type also in some situations where it is not a return type. In particular, we want to make it possible to write Future<void>. This will have two important consequences:

  1. Users can express their intent in a better way than with Future<Null>. Not only is Future<Null> not expressing the intended behavior, it is also assignable to every other Future (since Null is now at the bottom of the hierarchy). This is the exact opposite of what users want when they don't have a value.
  2. The type inference will be able to infer void. We have seen this fail most often in cases, where the argument to a function uses the return-type of an argument for inference. For example, zone.run(voidFunction).

The exact details are still under development, but this feature is very high on our priority list, and we have made some progress on it.

Enhanced Type Promotion

Dart uses is expressions in if conditions to promote the type of a variable within the corresponding branch: if (x is String) { x.toUpperCase(); }.

We found that the current promotion is very useful, but misses lots of common cases. For example, if (x is! A) throw "not an A"; would not promote x to be an A in the rest of the body.

Since the analyzer already had its own, more advanced, type promotion we reached out to the analyzer team, and @bwilkerson wrote a proposal (thanks!). We have been discussing this proposal in a pull request: https://github.com/dart-lang/sdk/pull/29624

Updates to the Core Libraries

As part of our OKRs we want to clean up the core libraries. This includes, deprecating some rarely used classes/methods, and adding functionality that should have been there in the first place.

This is still work in progress, and we don't have a list of planned changes yet, but here are some that are likely to make the cut:

  • Add a BigInt class to dart:typed_data. In Dart 2.0, integers will be fixed-sized, and this class provides a way to migrate code that relies on arbitrary-size integers.
  • Deprecate SplayTreeMap, SplayTreeSet, DoubleLinkedQueue and DoubleLinkedList. We are going to copy these classes to the collection package to ease the migration.