OLD | NEW |
(Empty) | |
| 1 # Dart Language and Library Newsletter |
| 2 2017-08-11 |
| 3 @floitschG |
| 4 |
| 5 Welcome to the Dart Language and Library Newsletter. |
| 6 |
| 7 ## Follow Ups |
| 8 |
| 9 ### Void Arrow Functions |
| 10 As mentioned in an earlier newsletter, `void` arrow functions with non-void expr
essions (as in `void foo() => x++`) are supported with Dart 1.24. However, this
feature still has to be used with care. Due to a temporary limitation of the typ
e inference in strong mode, returning a non-void expression might not work as ex
pected. |
| 11 |
| 12 For example: |
| 13 |
| 14 ``` dart |
| 15 var f = new Future(() { doSomethingAsynchronously(); }; |
| 16 f.catchError((e) => errorCounter++); |
| 17 ``` |
| 18 |
| 19 The type-inference algorithm currently infers `Null` for the generic type of the
`Future`. Functions without `return` indeed return `null`, so technically, that
type is correct. However, the `catchError` signature requires the provided func
tion to return the same type as the function it is attached to. In this case, `f
` is a `Future<Null>`, but `errorCounter++` is an `int`. Since `int` is not `Nul
l` this throws at runtime. |
| 20 |
| 21 As mentioned in earlier newsletters, we are actively working on generalizing `vo
id`, and once it is supported the inferred type of `f` will be `Future<void>`. T
he `catchError` closure then would just need to be a subtype of `void Function()
` which would work fine for `(e) => errorCounter++`. Until then, be careful wher
e you use the `void` arrow function syntax. |
| 22 |
| 23 ### Deferred Loading |
| 24 Last time we discussed our plans to allow the use of deferred types even when th
e deferred libraries haven't been loaded yet. This makes programs, like the foll
owing, possible: |
| 25 |
| 26 ``` dart |
| 27 /// lib1.dart |
| 28 class A {} |
| 29 |
| 30 /// lib2.dart |
| 31 export "lib1.dart" show A; |
| 32 |
| 33 /// main.dart |
| 34 import "lib1.dart"; |
| 35 import "lib2.dart" deferred as def; |
| 36 |
| 37 main() { |
| 38 print(new A() is def.A); // Requires knowledge of `def.A`. |
| 39 } |
| 40 ``` |
| 41 |
| 42 A follow-up mail questioned the need for such a big hammer. In reality, most pro
grams just want to use the deferred type as a type annotations: |
| 43 |
| 44 ``` dart |
| 45 main() async { |
| 46 def.A a; // <-- Illegal today. |
| 47 await def.loadLibrary(); |
| 48 a = new def.A(); |
| 49 } |
| 50 ``` |
| 51 |
| 52 Is there a simpler / better solution that would allow patterns like these, but n
ot require full knowledge of the deferred types? |
| 53 |
| 54 It turns out, that the answer is likely "no". In fact, we find that, because of
type inference, even the current behavior is already counterintuitive and should
be fixed. That is, even without allowing more uses of deferred types, programs
don't behave as expected: |
| 55 |
| 56 ``` dart |
| 57 // ------ def.dart |
| 58 class Box<T> { |
| 59 T value; |
| 60 Box(this.value); |
| 61 } |
| 62 |
| 63 // ------ main.dart |
| 64 import "def.dart" deferred as def; |
| 65 |
| 66 main() async { |
| 67 await def.loadLibrary(); |
| 68 var box = new def.Box(499); |
| 69 var list = [box.value]; |
| 70 } |
| 71 ``` |
| 72 |
| 73 With type inference, users expect three things to happen: |
| 74 1. `box` is of type `def.Box<int>`. |
| 75 2. the generic type of `new def.Box(499)` is `Box<int>`, as if the user had writ
ten `new def.Box<int>(499)`. |
| 76 3. `list` is of type `List<int>`. |
| 77 |
| 78 Without access to the deferred sources, none of these expectations is met. Since
type inference runs at compile-time, `box` has to be treated like `dynamic`. Th
ere is simply not more information available. For similar reasons, `box` must be
of type `Box<dynamic>`. Since the invocation of the constructor happens at runt
ime (where no type-inference happens), the missing generic type is dynamically f
illed with `dynamic`. |
| 79 |
| 80 Finally, `list` must be of type `List<dynamic>` since `box.value` is a dynamic i
nvocation, and the type inference doesn't know that the returned value will be o
f type `int`. |
| 81 |
| 82 This small example shows that type inference requires knowledge of the deferred
types to do its job. This means that all sources must be available when compilin
g individual libraries. Once that's the case it doesn't make sense to restrict t
he use of deferred types. They don't take up much space (which is the usual reas
on for deferring libraries), and giving full access to them removes a lot of boi
lerplate or dynamic code. |
| 83 |
| 84 ## Const Functions |
| 85 The language team discussed the possibility of supporting `const` functions. |
| 86 |
| 87 ``` dart |
| 88 class A { |
| 89 final Function(e) callback; |
| 90 const A(this.callback); |
| 91 } |
| 92 |
| 93 // Provide a `const` function to `A`'s constructor. |
| 94 const x = const A(const (e) { print(e); }); |
| 95 |
| 96 // Default values have to be `const`. |
| 97 void sort(List<int> list, [int compare(int x, int y) = const (x, y) => x - y) { |
| 98 ... |
| 99 } |
| 100 ``` |
| 101 |
| 102 This feature doesn't add new functionality. Users can already now write a static
function with the same body and use its tear-off (which is guaranteed to be `co
nst`) in all of these locations. However, it's more convenient to write function
s closer to where they are needed. For example, the classic `map.putIfAbsent(x,
() => [])` allocates a new function (a cheap operation, but still), whereas `map
.putIfAbsent(x, const () => [])` would always reuse the same function. |
| 103 |
| 104 Sidenote: in dart2js, many const values (not functions) are allocated at initial
ization, which shifts some execution time to the beginning of the program where
many teams already struggle with performance. In the current dart2js version it'
s thus not always beneficial to make objects `const`. |
| 105 |
| 106 ## Shadowing of Core Libraries |
| 107 When deprecating core library classes (like `SplayTreeMap`) we intend to minimiz
e the cost to our users. We copy the deprecated classes to packages (in this cas
e `collection`) so that users just need to change their imports from `dart:colle
ction` to `package:collection`. However, that means that programs that import `d
art:collection` and `package:collection` at the same time now see the same class
twice; once from each import. Which class should Dart now use? Is this an error
? |
| 108 |
| 109 For "normal" imports (not `dart:`), the rules are simple: an ambiguous reference
is an error. There is no good way to decide between class `A` of package `pkg1`
or `pkg2`. With core libraries, things get a bit more complicated: whereas upgr
ading packages is a user-triggered action (with the fallback to revert to the pr
evious `pubspec.lock`), upgrading the SDK should generally be safe. As a consequ
ence, Dart considers core libraries as less important. That is, shadowing a clas
s from any `dart:` library is ok. Importing `dart:collection` and `package:colle
ction/collection.dart` is thus fine and will not lead to errors. It's still good
practice to use `show` and `hide` to make the intention completely clear. |
| 110 |
| 111 We are still unsure how to handle cases when the user explicitly used `show` to
import a specific core library type: |
| 112 |
| 113 ``` dart |
| 114 import 'dart:collection` show SplayTreeMap; |
| 115 import 'package:collection/collection.dart'; |
| 116 ``` |
| 117 |
OLD | NEW |