mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
Newsletter 2017-08-18.
Review-Url: https://codereview.chromium.org/3001943002 .
This commit is contained in:
parent
783f1e8c04
commit
e6e8d35323
214
docs/newsletter/20170818.md
Normal file
214
docs/newsletter/20170818.md
Normal file
|
@ -0,0 +1,214 @@
|
|||
# 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.
|
||||
|
Loading…
Reference in a new issue