'Florian Loitsch' via Dart Misc
2017-08-11 15:35:25 UTC
Github link:
https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md
Earlier newsletters:
https://github.com/dart-lang/sdk/blob/d94cecba3ceaf6578315764a07292ea0d5e082c3/docs/
newsletter/20170804.md
https://github.com/dart-lang/sdk/blob/d94cecba3ceaf6578315764a07292ea0d5e082c3/docs/
newsletter/20170728.md
Dart Language and Library Newsletter
Welcome to the Dart Language and Library Newsletter.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#follow-ups>Follow
Ups
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#void-arrow-functions>Void
Arrow Functions
As mentioned in an earlier newsletter, void arrow functions with non-void
expressions (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 type inference in strong mode, returning a non-void
expression might not work as expected.
For example:
var f = new Future(() { doSomethingAsynchronously(); };
f.catchError((e) => errorCounter++);
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 function 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 Null this throws at runtime.
As mentioned in earlier newsletters, we are actively working on
generalizing void, and once it is supported the inferred type of f will be
Future<void>. The catchError closure then would just need to be a subtype
of void Function() which would work fine for (e) => errorCounter++. Until
then, be careful where you use the void arrow function syntax.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#deferred-loading>Deferred
Loading
Last time we discussed our plans to allow the use of deferred types even
when the deferred libraries haven't been loaded yet. This makes programs,
like the following, possible:
/// lib1.dartclass A {}
/// lib2.dartexport "lib1.dart" show A;
/// main.dartimport "lib1.dart";import "lib2.dart" deferred as def;
main() {
print(new A() is def.A); // Requires knowledge of `def.A`.
}
A follow-up mail questioned the need for such a big hammer. In reality,
most programs just want to use the deferred type as a type annotations:
main() async {
def.A a; // <-- Illegal today.
await def.loadLibrary();
a = new def.A();
}
Is there a simpler / better solution that would allow patterns like these,
but not require full knowledge of the deferred types?
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:
// ------ def.dartclass Box<T> {
T value;
Box(this.value);
}
// ------ main.dartimport "def.dart" deferred as def;
main() async {
await def.loadLibrary();
var box = new def.Box(499);
var list = [box.value];
}
With type inference, users expect three things to happen:
1. box is of type def.Box<int>.
2. the generic type of new def.Box(499) is Box<int>, as if the user had
written new def.Box<int>(499).
3. list is of type List<int>.
Without access to the deferred sources, none of these expectations is met.
Since type inference runs at compile-time, boxhas to be treated like dynamic.
There is simply not more information available. For similar reasons, box must
be of type Box<dynamic>. Since the invocation of the constructor happens at
runtime (where no type-inference happens), the missing generic type is
dynamically filled with dynamic.
Finally, list must be of type List<dynamic> since box.value is a dynamic
invocation, and the type inference doesn't know that the returned value
will be of type int.
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 compiling individual libraries. Once that's the case it doesn't make
sense to restrict the use of deferred types. They don't take up much space
(which is the usual reason for deferring libraries), and giving full access
to them removes a lot of boilerplate or dynamic code.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#const-functions>Const
Functions
The language team discussed the possibility of supporting const functions.
class A {
final Function(e) callback;
const A(this.callback);
}
// Provide a `const` function to `A`'s constructor.const x = const
A(const (e) { print(e); });
// Default values have to be `const`.void sort(List<int> list, [int
compare(int x, int y) = const (x, y) => x - y) {
...
}
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 const) in all of these locations. However, it's more
convenient to write functions 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.
Sidenote: in dart2js, many const values (not functions) are allocated at
initialization, 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.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#shadowing-of-core-libraries>Shadowing
of Core Libraries
When deprecating core library classes (like SplayTreeMap) we intend to
minimize the cost to our users. We copy the deprecated classes to packages
(in this case collection) so that users just need to change their imports
from dart:collection to package:collection. However, that means that
programs that import dart: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?
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 upgrading packages is a user-triggered action (with
the fallback to revert to the previous pubspec.lock), upgrading the SDK
should generally be safe. As a consequence, Dart considers core libraries
as less important. That is, shadowing a class from any dart: library is ok.
Importing dart:collection and package:collection/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.
We are still unsure how to handle cases when the user explicitly used show to
import a specific core library type:
import 'dart:collection` show SplayTreeMap;import
'package:collection/collection.dart';
https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md
Earlier newsletters:
https://github.com/dart-lang/sdk/blob/d94cecba3ceaf6578315764a07292ea0d5e082c3/docs/
newsletter/20170804.md
https://github.com/dart-lang/sdk/blob/d94cecba3ceaf6578315764a07292ea0d5e082c3/docs/
newsletter/20170728.md
Dart Language and Library Newsletter
Welcome to the Dart Language and Library Newsletter.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#follow-ups>Follow
Ups
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#void-arrow-functions>Void
Arrow Functions
As mentioned in an earlier newsletter, void arrow functions with non-void
expressions (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 type inference in strong mode, returning a non-void
expression might not work as expected.
For example:
var f = new Future(() { doSomethingAsynchronously(); };
f.catchError((e) => errorCounter++);
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 function 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 Null this throws at runtime.
As mentioned in earlier newsletters, we are actively working on
generalizing void, and once it is supported the inferred type of f will be
Future<void>. The catchError closure then would just need to be a subtype
of void Function() which would work fine for (e) => errorCounter++. Until
then, be careful where you use the void arrow function syntax.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#deferred-loading>Deferred
Loading
Last time we discussed our plans to allow the use of deferred types even
when the deferred libraries haven't been loaded yet. This makes programs,
like the following, possible:
/// lib1.dartclass A {}
/// lib2.dartexport "lib1.dart" show A;
/// main.dartimport "lib1.dart";import "lib2.dart" deferred as def;
main() {
print(new A() is def.A); // Requires knowledge of `def.A`.
}
A follow-up mail questioned the need for such a big hammer. In reality,
most programs just want to use the deferred type as a type annotations:
main() async {
def.A a; // <-- Illegal today.
await def.loadLibrary();
a = new def.A();
}
Is there a simpler / better solution that would allow patterns like these,
but not require full knowledge of the deferred types?
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:
// ------ def.dartclass Box<T> {
T value;
Box(this.value);
}
// ------ main.dartimport "def.dart" deferred as def;
main() async {
await def.loadLibrary();
var box = new def.Box(499);
var list = [box.value];
}
With type inference, users expect three things to happen:
1. box is of type def.Box<int>.
2. the generic type of new def.Box(499) is Box<int>, as if the user had
written new def.Box<int>(499).
3. list is of type List<int>.
Without access to the deferred sources, none of these expectations is met.
Since type inference runs at compile-time, boxhas to be treated like dynamic.
There is simply not more information available. For similar reasons, box must
be of type Box<dynamic>. Since the invocation of the constructor happens at
runtime (where no type-inference happens), the missing generic type is
dynamically filled with dynamic.
Finally, list must be of type List<dynamic> since box.value is a dynamic
invocation, and the type inference doesn't know that the returned value
will be of type int.
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 compiling individual libraries. Once that's the case it doesn't make
sense to restrict the use of deferred types. They don't take up much space
(which is the usual reason for deferring libraries), and giving full access
to them removes a lot of boilerplate or dynamic code.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#const-functions>Const
Functions
The language team discussed the possibility of supporting const functions.
class A {
final Function(e) callback;
const A(this.callback);
}
// Provide a `const` function to `A`'s constructor.const x = const
A(const (e) { print(e); });
// Default values have to be `const`.void sort(List<int> list, [int
compare(int x, int y) = const (x, y) => x - y) {
...
}
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 const) in all of these locations. However, it's more
convenient to write functions 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.
Sidenote: in dart2js, many const values (not functions) are allocated at
initialization, 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.
<https://github.com/dart-lang/sdk/blob/1daab67666ce4f7fe5f04b1349000a5492af6302/docs/newsletter/20170811.md#shadowing-of-core-libraries>Shadowing
of Core Libraries
When deprecating core library classes (like SplayTreeMap) we intend to
minimize the cost to our users. We copy the deprecated classes to packages
(in this case collection) so that users just need to change their imports
from dart:collection to package:collection. However, that means that
programs that import dart: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?
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 upgrading packages is a user-triggered action (with
the fallback to revert to the previous pubspec.lock), upgrading the SDK
should generally be safe. As a consequence, Dart considers core libraries
as less important. That is, shadowing a class from any dart: library is ok.
Importing dart:collection and package:collection/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.
We are still unsure how to handle cases when the user explicitly used show to
import a specific core library type:
import 'dart:collection` show SplayTreeMap;import
'package:collection/collection.dart';
--
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.
For other discussions, see https://groups.google.com/a/dartlang.org/
For HOWTO questions, visit http://stackoverflow.com/tags/dart
To file a bug report or feature request, go to http://www.dartbug.com/new
---
You received this message because you are subscribed to the Google Groups "Dart Misc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to misc+***@dartlang.org.