OLD | NEW |
1 ## 1.22.0 | 1 ## 1.22.0 |
2 | 2 |
3 ### Language | 3 ### Language |
4 | 4 |
5 * Breaking change: ['Generalized tear-offs'](https://github.com/gbracha/genera
lizedTearOffs/blob/master/proposal.md) | 5 * Breaking change: ['Generalized tear-offs'](https://github.com/gbracha/genera
lizedTearOffs/blob/master/proposal.md) |
6 are no longer supported, and will cause errors. We updated the language spec | 6 are no longer supported, and will cause errors. We updated the language spec |
7 and added warnings in 1.21, and are now taking the last step to fully | 7 and added warnings in 1.21, and are now taking the last step to fully |
8 de-support them. They were previously supported in the VM only. | 8 de-support them. They were previously only supported in the VM, and there |
| 9 are almost no known uses of them in the wild. |
9 | 10 |
10 * The `assert()` statement has been expanded to support an optional second | 11 * The `assert()` statement has been expanded to support an optional second |
11 `message` argument (SDK issue [27342](https://github.com/dart-lang/sdk/issue
s/27342)). | 12 `message` argument (SDK issue [27342](https://github.com/dart-lang/sdk/issue
s/27342)). |
12 | 13 |
13 The message is displayed if the assert fails. It can be any object, and it | 14 The message is displayed if the assert fails. It can be any object, and it |
14 is accessible as `AssertionError.message`. It can be used to provide more | 15 is accessible as `AssertionError.message`. It can be used to provide more |
15 user friendly exception outputs. As an example, the following assert: | 16 user friendly exception outputs. As an example, the following assert: |
16 | 17 |
17 ```dart | 18 ```dart |
18 assert(configFile != null, "Tool config missing. Please see https://goo.gl/k
8iAi for details."); | 19 assert(configFile != null, "Tool config missing. Please see https://goo.gl/k
8iAi for details."); |
19 ``` | 20 ``` |
20 | 21 |
21 would produce the following exception output: | 22 would produce the following exception output: |
22 | 23 |
23 ``` | 24 ``` |
24 Unhandled exception: | 25 Unhandled exception: |
25 'file:///Users/mit/tmp/tool/bin/main.dart': Failed assertion: line 9 pos 10: | 26 'file:///Users/mit/tmp/tool/bin/main.dart': Failed assertion: line 9 pos 10: |
26 'configFile != null': Tool config missing. Please see https://goo.gl/k8iAi f
or details. | 27 'configFile != null': Tool config missing. Please see https://goo.gl/k8iAi f
or details. |
27 #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:33) | 28 #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:33) |
28 #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:29) | 29 #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:29) |
29 #2 main (file:///Users/mit/tmp/tool/bin/main.dart:9:10) | 30 #2 main (file:///Users/mit/tmp/tool/bin/main.dart:9:10) |
30 ``` | 31 ``` |
31 | 32 |
32 * The `Null` type has been moved to the bottom of the type hierarchy. As such, | 33 * The `Null` type has been moved to the bottom of the type hierarchy. As such, |
33 it is considered a subtype of every other type. | 34 it is considered a subtype of every other type. The `null` *literal* was |
| 35 always treated as a bottom type. Now the named class `Null` is too: |
34 | 36 |
35 Examples: | 37 ```dart |
| 38 const empty = <Null>[]; |
| 39 |
| 40 String concatenate(List<String> parts) => parts.join(); |
| 41 int sum(List<int> numbers) => numbers.fold(0, (sum, n) => sum + n); |
| 42 |
| 43 concatenate(empty); // OK. |
| 44 sum(empty); // OK. |
36 ``` | 45 ``` |
37 Null foo() => null; | |
38 int x = foo(); | |
39 String x = foo(); | |
40 | 46 |
41 List<Null> bar() => <Null>[]; | 47 * Introduce `covariant` modifier on parameters. It indicates that the |
42 List<int> = bar(); | 48 parameter (and the corresponding parameter in any method that overrides it) |
43 List<String> = bar(); | 49 has looser override rules. In strong mode, these require a runtime type |
| 50 check to maintain soundness, but enable an architectural pattern that is |
| 51 useful in some code. |
| 52 |
| 53 It lets you specialize a family of classes together, like so: |
| 54 |
| 55 ```dart |
| 56 abstract class Predator { |
| 57 void chaseAndEat(covariant Prey p); |
| 58 } |
| 59 |
| 60 abstract class Prey {} |
| 61 |
| 62 class Mouse extends Prey {} |
| 63 |
| 64 class Seal extends Prey {} |
| 65 |
| 66 class Cat extends Predator { |
| 67 void chaseAndEat(Mouse m) => ... |
| 68 } |
| 69 |
| 70 class Orca extends Predator { |
| 71 void chaseAndEat(Seal s) => ... |
| 72 } |
44 ``` | 73 ``` |
45 | 74 |
| 75 This isn't statically safe, because you could do: |
| 76 |
| 77 ```dart |
| 78 Predator predator = new Cat(); // Upcast. |
| 79 predator(new Seal()); // Cats can't eat seals! |
| 80 ``` |
| 81 |
| 82 To preserve soundness in strong mode, in the body of a method that uses a |
| 83 covariant override (here, `Cat.chaseAndEat()`), the compiler automatically |
| 84 inserts a check that the parameter is of the expected type. So the compiler |
| 85 gives you something like: |
| 86 |
| 87 ```dart |
| 88 class Cat extends Predator { |
| 89 void chaseAndEat(o) { |
| 90 var m = o as Mouse; |
| 91 ... |
| 92 } |
| 93 } |
| 94 ``` |
| 95 |
| 96 Spec mode allows this unsound behavior on all parameters, even though users |
| 97 rarely rely on it. Strong mode disallowed it initially. Now, strong mode |
| 98 lets you opt into this behavior in the places where you do want it by using |
| 99 this modifier. Outside of strong mode, the modifier is ignored. |
| 100 |
| 101 * Change instantiate-to-bounds rules for generic type parameters when running |
| 102 in strong mode. If you leave off the type parameters from a generic type, we |
| 103 need to decide what to fill them in with. Dart 1.0 says just use `dynamic`, |
| 104 but that isn't sound: |
| 105 |
| 106 ```dart |
| 107 class Abser<T extends num> { |
| 108 void absThis(T n) { n.abs(); } |
| 109 } |
| 110 |
| 111 var a = new Abser(); // Abser<dynamic>. |
| 112 a.absThis("not a num"); |
| 113 ``` |
| 114 |
| 115 We want the body of `absThis()` to be able to safely assume `n` is at |
| 116 least a `num` -- that's why there's a constraint on T, after all. Implicitly |
| 117 using `dynamic` as the type parameter in this example breaks that. |
| 118 |
| 119 Instead, strong mode uses the bound. In the above example, it fills it in |
| 120 with `num`, and then the second line where a string is passed becomes a |
| 121 static error. |
| 122 |
| 123 However, there are some cases where it is hard to figure out what that |
| 124 default bound should be: |
| 125 |
| 126 ```dart |
| 127 class RuhRoh<T extends Comparable<T>> {} |
| 128 ``` |
| 129 |
| 130 Strong mode's initial behavior sometimes produced surprising, unintended |
| 131 results. For 1.22, we take a simpler approach and then report an error if |
| 132 a good default type argument can't be found. |
| 133 |
| 134 ### Core libraries |
| 135 |
| 136 * Define `FutureOr<T>` for code that works with either a future or an |
| 137 immediate value of some type. For example, say you do a lot of text |
| 138 manipulation, and you want a handy function to chain a bunch of them: |
| 139 |
| 140 ```dart |
| 141 typedef String StringSwizzler(String input); |
| 142 |
| 143 String swizzle(String input, List<StringSwizzler> swizzlers) { |
| 144 var result = input; |
| 145 for (var swizzler in swizzlers) { |
| 146 result = swizzler(result); |
| 147 } |
| 148 |
| 149 return result; |
| 150 } |
| 151 ``` |
| 152 |
| 153 This works fine: |
| 154 |
| 155 ```dart |
| 156 main() { |
| 157 var result = swizzle("input", [ |
| 158 (s) => s.toUpperCase(), |
| 159 (s) => () => s * 2) |
| 160 ]); |
| 161 print(result); // "INPUTINPUT". |
| 162 } |
| 163 ``` |
| 164 |
| 165 Later, you realize you'd also like to support swizzlers that are |
| 166 asynchronous (maybe they look up synonyms for words online). You could make |
| 167 your API strictly asynchronous, but then users of simple synchronous |
| 168 swizzlers have to manually wrap the return value in a `Future.value()`. |
| 169 Ideally, your `swizzle()` function would be "polymorphic over asynchrony". |
| 170 It would allow both synchronous and asynchronous swizzlers. Because `await` |
| 171 accepts immediate values, it is easy to implement this dynamically: |
| 172 |
| 173 ```dart |
| 174 Future<String> swizzle(String input, List<StringSwizzler> swizzlers) async { |
| 175 var result = input; |
| 176 for (var swizzler in swizzlers) { |
| 177 result = await swizzler(result); |
| 178 } |
| 179 |
| 180 return result; |
| 181 } |
| 182 |
| 183 main() async { |
| 184 var result = swizzle("input", [ |
| 185 (s) => s.toUpperCase(), |
| 186 (s) => new Future.delayed(new Duration(milliseconds: 40), () => s * 2) |
| 187 ]); |
| 188 print(await result); |
| 189 } |
| 190 ``` |
| 191 |
| 192 What should the declared return type on StringSwizzler be? In the past, you |
| 193 had to use `dynamic` or `Object`, but that doesn't tell the user much. Now, |
| 194 you can do: |
| 195 |
| 196 ```dart |
| 197 typedef FutureOr<String> StringSwizzler(String input); |
| 198 ``` |
| 199 |
| 200 Like the name implies, `FutureOr<String>` is a union type. It can be a |
| 201 `String` or a `Future<String>`, but not anything else. In this case, that's |
| 202 not super useful beyond just stating a more precise type for readers of the |
| 203 code. It does give you a little better error checking in code that uses the |
| 204 result of that. |
| 205 |
| 206 `FutureOr<T>` becomes really important in *generic* methods like |
| 207 `Future.then()`. In those cases, having the type system understand this |
| 208 magical union type helps type inference figure out the type argument of |
| 209 `then()` based on the closure you pass it. |
| 210 |
| 211 Previously, strong mode had hard-coded rules for handling `Future.then()` |
| 212 specifically. `FutureOr<T>` exposes that functionality so third-party APIs |
| 213 can take advantage of it too. |
| 214 |
46 ### Tool changes | 215 ### Tool changes |
47 | 216 |
48 * Dart2Js | 217 * Dart2Js |
49 | 218 |
50 * Remove support for (long-time deprecated) mixin typedefs. | 219 * Remove support for (long-time deprecated) mixin typedefs. |
51 | 220 |
52 * Pub | 221 * Pub |
53 | 222 |
54 * Avoid using a barback asset server for executables unless they actually use | 223 * Avoid using a barback asset server for executables unless they actually use |
55 transformers. This makes precompilation substantially faster, produces | 224 transformers. This makes precompilation substantially faster, produces |
(...skipping 1433 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1489 they will keep the Dart process alive until they time out. This fixes the | 1658 they will keep the Dart process alive until they time out. This fixes the |
1490 handling of persistent connections. Previously, the client would shut down | 1659 handling of persistent connections. Previously, the client would shut down |
1491 immediately after a request. | 1660 immediately after a request. |
1492 | 1661 |
1493 * **Breaking change:** `HttpServer` no longer compresses all traffic by | 1662 * **Breaking change:** `HttpServer` no longer compresses all traffic by |
1494 default. The new `autoCompress` property can be set to `true` to re-enable | 1663 default. The new `autoCompress` property can be set to `true` to re-enable |
1495 compression. | 1664 compression. |
1496 | 1665 |
1497 * `dart:isolate`: `Isolate.spawnUri` added the optional `packageRoot` argument, | 1666 * `dart:isolate`: `Isolate.spawnUri` added the optional `packageRoot` argument, |
1498 which controls how it resolves `package:` URIs. | 1667 which controls how it resolves `package:` URIs. |
OLD | NEW |