mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
e6e8d35323
Review-Url: https://codereview.chromium.org/3001943002 .
215 lines
8.9 KiB
Markdown
215 lines
8.9 KiB
Markdown
# Dart Language and Library Newsletter
|
|
2017-08-18
|
|
@floitschG
|
|
|
|
Welcome to the Dart Language and Library Newsletter.
|
|
|
|
## If you missed it
|
|
Did you know that you can write trailing commas to arguments and parameters? This feature was added to the specification about a year ago.
|
|
|
|
It's main use-case is to unify parameter and argument lists that span multiple lines. For example, [Flutter] uses it extensively to keep tree-like instantiations nicely aligned:
|
|
|
|
[Flutter]: http://flutter.io
|
|
|
|
``` dart
|
|
return new Material(
|
|
// Column is a vertical, linear layout.
|
|
child: new Column(
|
|
children: <Widget>[
|
|
new MyAppBar(
|
|
title: new Text(
|
|
'Example title',
|
|
style: Theme.of(context).primaryTextTheme.title,
|
|
),
|
|
),
|
|
new Expanded(
|
|
child: new Center(
|
|
child: new Text('Hello, world!'),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
```
|
|
|
|
Note how every argument list ends with a comma. The `dartfmt` tool knows about these trailing commas and ensures that the individual entries stay on their own lines so that it is easy to move them around with cut and paste.
|
|
|
|
Recently, we also updated the specification to allow trailing commas in `assert`s. This makes the syntax of `assert`s more consistent with function calls.
|
|
|
|
### Function Type Syntax
|
|
A few months ago, we added a new function type syntax to Dart (we mentioned it in our first newsletter).
|
|
|
|
``` dart
|
|
// Examples:
|
|
typedef F = void Function(int); // A void function that takes an int.
|
|
|
|
void foo(T Function<T>(T x) f) { // foo takes a generic function.
|
|
...
|
|
}
|
|
|
|
class A {
|
|
// A has a field `f` of type function that takes a String and returns void.
|
|
void Function(String) f;
|
|
}
|
|
```
|
|
|
|
Before we added the new function-type syntaxes, we evaluated multiple options. In this section I will summarize some of the discussions we had.
|
|
|
|
#### Motivation
|
|
The new function-type syntax intends to solve three issues:
|
|
|
|
1. the old `typedef` syntax doesn't support generic types.
|
|
2. the old function syntax can't be used for fields and locals.
|
|
3. in the old syntax, providing only one identifier in an argument position is interpreted as name and not type. For example: `typedef f(int);` is *not* a typedef for a function that expects an `int`, but for a function that expects `dynamic` and names the argument "int".
|
|
|
|
With Dart 2.0 we will support generic methods, and also generic closures. This means that a function can accept a generic function as argument. We were lacking a way to express this type.
|
|
|
|
Dart 1.x has two ways to express function types: a) an inline syntax for parameters and b) `typedef`s.
|
|
|
|
It was easy to extend the inline syntax to support generic arguments:
|
|
|
|
|
|
``` dart
|
|
// Takes a function `factoryForA` that is generic on T.
|
|
void foo(A<T> factoryForA<T>()) {
|
|
A<int> x = factoryForA<int>();
|
|
A<String> x = factoryForA<String>();
|
|
}
|
|
```
|
|
|
|
However, there was no easy way to do the same for `typedef`s:
|
|
|
|
``` dart
|
|
typedef A<T> FactoryForA<T>();// Does *not* do what we want it to do:
|
|
|
|
FactoryForA f; // A function that returns an `A<dynamic>`.
|
|
FactoryForA<String> f2; // A function that returns an `A<String>`.
|
|
f<int>(); // Error: `f` is not generic.
|
|
```
|
|
|
|
We had already used the most consistent place for the generic method argument as a template argument to the `typedef` itself. If we could go back in time, we could change it as follows:
|
|
|
|
``` dart
|
|
typedef<T> List<T> TemplateTypedef();
|
|
TemplateTypedef<int> f; // A function that returns a List<int>.
|
|
TemplateTypedef f; // A function that returns a List<dynamic>.
|
|
|
|
typedef List<T> GenericTypedef<T>();
|
|
GenericTypedef f; // A function that is generic.
|
|
List<int> ints = f<int>();
|
|
List<String> strings = f<String>();
|
|
```
|
|
|
|
Given that this would be a breaking change we explored alternatives that would also solve the other two issues. In particular the new syntax had to work for locals and fields, too.
|
|
|
|
First and foremost the new syntax had to be readable. It also had to solve the three mentioned issues. Finally, we wanted to make sure, we didn't choose a syntax that would hinder future evolution of the language. We made sure that the syntax would work with:
|
|
- nullability: the syntax must be nullable without too much hassle:
|
|
``` dart
|
|
(int)->int?; // A function that is nullable, or that returns a nullable integer?
|
|
Problem disappears with <-
|
|
int <- (int)? ; vs int? <- (int)
|
|
```
|
|
- union types (in case we ever want them).
|
|
|
|
|
|
#### Common Characteristics
|
|
For all the following proposals we had decided that the arguments could either be just the type, or optionally have a name. For example, `(int)->int` is equivalent to `(int id)->int`. Especially with multiple arguments of the same type, providing a name can make it much easier to reason about the type: `(int id, int priority) -> void`. However, type-wise these parameter names are ignored.
|
|
|
|
All of the proposals thus interpret single-argument identifiers as types. This is in contrast to the old syntax where a single identifier would state the name of the parameter: in `void foo(bar(int)) {...}` the `int` is the name of the parameter to `bar`. This discrepancy is hopefully temporary, as we intend to eventually change the behavior of the old syntax.
|
|
|
|
##### Right -> Arrow
|
|
Using `->` as function-type syntax feels very natural and is used in many other languages: Swift, F#, SML, OCaml, Haskell, Miranda, Coq, Kotlin, and Scala (with =>).
|
|
|
|
Examples:
|
|
``` dart
|
|
typedef F = (int) -> void; // Function from (int) to void.
|
|
typedef F<T> = () -> List<T>; // Template Typedef.
|
|
typedef F = <T>(T) -> List<T>; // Generic function from T to List<T>.
|
|
```
|
|
|
|
We could even allow a short form when there is only one argument: `int->int`.
|
|
|
|
We have experimented with this syntax: [https://codereview.chromium.org/2439573003/]
|
|
|
|
Advantages:
|
|
- easy to learn and familiar to many developers.
|
|
- could support shorthand form `int->int`.
|
|
|
|
Open questions:
|
|
- support shorthand form?
|
|
- whitespace. Should it be `(int, int) -> String` or `(int, int)->String`, etc.
|
|
|
|
Disadvantages:
|
|
- Relatively late token. The parser would have to do relatively big lookaheads.
|
|
- Works badly with nullable types:
|
|
``` dart
|
|
typedef F = (int) -> int?; // Nullable function or nullable int?
|
|
// Could be disambiguated as follows:
|
|
typedef F = ((int)->int)?; // Clearly nullable function.
|
|
```
|
|
|
|
|
|
##### Left <- Arrow
|
|
This section explores using `<-` as function-type syntax. There is at least one other language that uses this syntax: [Twelf](http://www.cs.cmu.edu/~twelf/guide-1-2/twelf_3.html).
|
|
|
|
Examples:
|
|
``` dart
|
|
typedef F = void <- (int); // Function from (int) to void.
|
|
typedef F<T> = List<T> <- (); // Template Typedef.
|
|
typedef F = List<T> <- <T>(T); // Generic function from T to List<T>.
|
|
```
|
|
|
|
Could also allow a short form: `int<-int`. (For some reason this seems to read less nicely than `int->int`.)
|
|
|
|
We have experimented with this syntax: [https://codereview.chromium.org/2466393002/]
|
|
|
|
Advantages:
|
|
- return value is on the left, similar to normal function signatures. This also simplifies `typedef`s, where the return value is more likely to stay on the first line.
|
|
- faster to parse, since the `<-` doesn't require a lot of look-ahead.
|
|
- relatively similar to `->`.
|
|
- no problems with nullable types:
|
|
``` dart
|
|
typedef F = int <- (int)?; // Nullable function.
|
|
typedef F = int? <- (int); // Returns nullable int.
|
|
```
|
|
|
|
Open Questions:
|
|
- whitespace?
|
|
- support shorthand form?
|
|
|
|
Disadvantages:
|
|
- `<-` is ambiguous: `x<-y ? foo(x) : foo(y) // if x < (-y) ...`.
|
|
- Not as familiar as `->`.
|
|
|
|
##### Function
|
|
Dart already uses `Function` as general type for functions. It is relatively straightforward to extend the use of `Function` to include return and parameter types. (And no: it's not `Function<int, int>` since that wouldn't work for named arguments).
|
|
|
|
|
|
``` dart
|
|
typedef F = void Function(int); // Function from (int) to void.
|
|
typedef F<T> = List<T> Function(); // Template Typedef.
|
|
typedef F = List<T> Function<T>(T); // Generic function from T to List<T>.
|
|
```
|
|
|
|
This form does not allow any shorthand syntax, but fits nicely into the existing parameter syntax.
|
|
|
|
Before we accepted this syntax, we had experimented with this syntax: [https://codereview.chromium.org/2482923002/]
|
|
|
|
Advantages:
|
|
- very similar to the syntax of the corresponding function declarations.
|
|
- no ambiguity.
|
|
- (almost) no new syntax. That is, the type can be immediately extrapolated from other syntax.
|
|
- no open questions wrt whitespace.
|
|
- symmetries with existing use of `Function`:
|
|
``` dart
|
|
Function f; // a function.
|
|
Function(int x) f; // a function that takes an int.
|
|
double Function(int) f; // a function that takes an int and returns a double.
|
|
```
|
|
|
|
Disadvantages:
|
|
- longer.
|
|
|
|
##### Conclusion
|
|
We found that the `Function`-based syntax fits nicely into Dart and fulfills all of our requirements. Due to its similarity to function declarations it is also very future-proof. Any feature that works with function declarations should work with the `Function`-type syntax, as well.
|
|
|