'Florian Loitsch' via Dart Misc
2017-09-08 19:39:56 UTC
Github link:
https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md
Earlier newsletters:
https://github.com/dart-lang/sdk/tree/master/docs/newsletter
Dart Language and Library Newsletter
Welcome to the Dart Language and Library Newsletter.
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#follow-up---call>Follow-Up
- Call
Last newsletter we announced our desire to remove the call operator from
the language. We got some feedback that showed some uses in the wild.
Please keep them coming. It will definitely influence our decision whether
(or when) we are going to remove the operator.
We also forgot an additional benefit of removing the operator: since users
wouldn't be able to implement functions by themselves we could extend the
Function interface with useful properties. For example, we could add
getters that return the arity / signature of the receiver closure:
void onError(Function errorHandler) {
// positionalArgumentCount includes optional positional parameters.
if (errorHandler.positionalParameterCount >= 2) {
errorHandler(error, stackTrace);
} else {
errorHandler.positionalParameterCount == 1);
errorHandler(error);
}
}
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#fuzzy-arrow>Fuzzy
Arrow
In Dart 1.x dynamic was used for both the *top* and *bottom* of the typing
hierarchy. Depending on the context, dynamic could either mean Object (top)
or Null (bottom). For the remainder of the section remember that every type
is a subtype of Object (which is why it's called "top"), and every type is
a supertype of Null.
This schizophrenic interpretation of dynamic can be observed easily with
generic types:
void main() {
print(<int>[1, 2, 3] is List<dynamic>); // Use `dynamic` as bottom. => true.
print(<dynamic>[1, 2, 3] is List<int>); // Use `dynamic` as top. => true.
}
In the first statement, List<dynamic> is used as a supertype of List<int>,
whereas in the second statement, List<dynamic> is used as subtype of
List<int>. This works for every type and not just int. As such, dynamic clearly
is top and bottom at the same time.
With strong mode, this dual-view of dynamic became an issue, and, for the
sake of soundness, dynamic was downgraded to Object. It still supports
dynamic calls, but can't be used as bottom anymore. In strong mode, the
second statement thus prints "false". However, strong mode kept one small
exception: *fuzzy arrows*.
The fuzzy arrow exception allows dynamic to be used as if it was bottom when
it is used in function types. Take the following example:
/// Fills [list2] with the result of applying [f] to every element
of/// [list1] (if [f] is of arity 1), or of applying [f] to every///
element of [list1] and [list2] (otherwise).void map1or2(Function f,
List list1, List list2) {
for (int i = 0; i < list1.length; i++) {
var x = list1[i];
if (f is Function(dynamic)) {
list2[i] = f(x);
} else {
var y = list2[i];
list2[i] = f(x, y);
}
}
}
int square(int x) => x * x;
void main() {
var list1 = <int>[1, 2, 3];
var list2 = new List(3);
map1or2(square, list1, list2);
print(list2);
}
This code is relatively dynamic and avoids lots of types (and in particular
generic arguments to map1or2), but it is a correct strong mode program. In
DDC it prints [1, 4, 9].
There are some implicit dynamics in the program, but we are really
interested in the one explicit dynamic in the function-type test: if (f is
Function(dynamic)). Intuitively, that test looks good: we don't mind which
type the function takes and thus wrote dynamic for the parameter type.
However, that wouldn't work if dynamic was interpreted as Object. In that
case, the is check asks whether the provided f could be invoked with *any*
Object. That's not what we want. We don't want to invoke it with a random
object value that we found somewhere, but invoke it with the values from
list1. It's the caller's responsibility to make sure that the types match.
In fact, we don't care for the type at all. The is check is just here to
test for the arity.
Since checking for arity is a common pattern, strong mode still treats
dynamic as bottom in this context. This function types is thus equivalent
to an arity check.
For a long time, the fuzzy arrow exception was necessary. Dart didn't have
any other way to do arity checking. Only with the move of the Null type to
the bottom of the typing hierarchy, was it possible to explicitly use the
bottom type instead of just dynamic. A sounder way of asking for a
function's arity is thus:
if (f is Function(Null)) {
This can be read as: "is f a function that takes at least null?". Without
non-nullable types *every* 1-arity function takes null and this test is
equivalent to asking whether the function takes one argument.
<footnote> With non-nullable types, the bottom type would need to change,
since there are types that wouldn't accept null anymore. At that point we
would need to introduce a Nothing type, and the is-check would need to be
rewritten to if (f is Function(Nothing)). Admittedly, the spoken
interpretation doesn't sound as logical anymore: "is f a function that
takes at least Nothing?" </footnote>
Since there is now a "correct" way of testing for the arity of functions,
the language team recently started to investigate whether we could drop the
fuzzy arrow exception from strong mode (and thus Dart 2.0).
Although the removal of fuzzy errors leads to breakages, our experience is
pretty positive so far. The biggest problems arise in cases where the
current type system is too weak to provide a correct replacement. Among
those, Map.fromIterablesclearly makes the biggest problems. The old
signature of that constructor is Map.fromIterable(Iterable iterable, {K
key(element), V value(element)}). Implicitly, both functions for key and
value take dynamic arguments and use the fuzzy arrow exceptions to support
iterables of any kind.
Without the fuzzy arrow exception the implicit dynamic in those types is
read similar to Object, thus requiring users to provide functions that can
deal with *any* object (and not just the ones from the iterable).
Unfortunately, our trick of replacing the dynamic with Null doesn't work
here:
Map.fromIterable(Iterable iterable,
{K key(Null element), V value(Null element)}) {
...
}
// Works when the argument is not a function literal:new Map<int,
String>.fromIterable(["1", "2"], keys: int.parse);
// Doesn't work, with function literal:new Map<int,
String>.fromIterable([1, 2], values: (x) => x.toString());
The reason the second instantiation doesn't work is that Dart uses the
context of a function literal to infer the parameter type. In this case the
literal (x) => x.toString() is used in a context where a V Function(Null) is
expected, and the literal is thus automatically adapted to satisfy this
signature: String Function(Null). However, that means that any invocation
of this function with a value that is not null yields to a dynamic error.
The correct way to fix this constructor is to allow generic arguments for
constructors:
Map.fromIterable<T>(Iterable<T> iterable,
{K key(T element), V value(T element)}) {
...
}
Supporting generic arguments for constructors is on our roadmap, but will
not make it for Dart 2.0. In the meantime we either have to live with
requiring functions that take objects, or we will have to change the key
and value type annotation to Function, thus losing the arity and type
information:
Map.fromIterable(Iterable iterable, {Function key, Function value}) {
...
}
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#enhanced-type-promotion>Enhanced
Type Promotion
As mentioned in a previous newsletter: one of our goals is to improve
Dart's type promotion. We want to make better use of is and is! checks. For
example, promote x to int after the if in the following code: if (x is!
int) throw x;.
When the language team discussed this topic we looked at the conditions
under which type promotion would be useful and intuitive. One of the
current restrictions is that promoted variables may not be assigned again:
void foo(Object x) {
if (x is String) {
x = x.subString(1); // Error: subString is not defined for Object.
print(x + "suffix");
}
}
void bar(Object x) {
if (x is WrappedInt) {
x = x.value; // Error: `value` is not defined for Object.
}
assert(x is int);
}
As can be seen in these two examples, assignments would require an analysis
that deals with flow-control, and that assigns potentially different types
to the same variable. Inside foo the user wants to continue using x as
String, whereas in barthe user wants to use x as an Object after the
assignment.
We have discussed multiple approaches to provide the correct, intuitive
behavior in these cases, and we are confident that we can provide a
solution that will work in most cases. However, we don't want to delay or
block the "easy" improvements, and therefore decided to exclude assignments
from the current proposal. We will come back to assignments of promoted
variables in the future.
https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md
Earlier newsletters:
https://github.com/dart-lang/sdk/tree/master/docs/newsletter
Dart Language and Library Newsletter
Welcome to the Dart Language and Library Newsletter.
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#follow-up---call>Follow-Up
- Call
Last newsletter we announced our desire to remove the call operator from
the language. We got some feedback that showed some uses in the wild.
Please keep them coming. It will definitely influence our decision whether
(or when) we are going to remove the operator.
We also forgot an additional benefit of removing the operator: since users
wouldn't be able to implement functions by themselves we could extend the
Function interface with useful properties. For example, we could add
getters that return the arity / signature of the receiver closure:
void onError(Function errorHandler) {
// positionalArgumentCount includes optional positional parameters.
if (errorHandler.positionalParameterCount >= 2) {
errorHandler(error, stackTrace);
} else {
errorHandler.positionalParameterCount == 1);
errorHandler(error);
}
}
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#fuzzy-arrow>Fuzzy
Arrow
In Dart 1.x dynamic was used for both the *top* and *bottom* of the typing
hierarchy. Depending on the context, dynamic could either mean Object (top)
or Null (bottom). For the remainder of the section remember that every type
is a subtype of Object (which is why it's called "top"), and every type is
a supertype of Null.
This schizophrenic interpretation of dynamic can be observed easily with
generic types:
void main() {
print(<int>[1, 2, 3] is List<dynamic>); // Use `dynamic` as bottom. => true.
print(<dynamic>[1, 2, 3] is List<int>); // Use `dynamic` as top. => true.
}
In the first statement, List<dynamic> is used as a supertype of List<int>,
whereas in the second statement, List<dynamic> is used as subtype of
List<int>. This works for every type and not just int. As such, dynamic clearly
is top and bottom at the same time.
With strong mode, this dual-view of dynamic became an issue, and, for the
sake of soundness, dynamic was downgraded to Object. It still supports
dynamic calls, but can't be used as bottom anymore. In strong mode, the
second statement thus prints "false". However, strong mode kept one small
exception: *fuzzy arrows*.
The fuzzy arrow exception allows dynamic to be used as if it was bottom when
it is used in function types. Take the following example:
/// Fills [list2] with the result of applying [f] to every element
of/// [list1] (if [f] is of arity 1), or of applying [f] to every///
element of [list1] and [list2] (otherwise).void map1or2(Function f,
List list1, List list2) {
for (int i = 0; i < list1.length; i++) {
var x = list1[i];
if (f is Function(dynamic)) {
list2[i] = f(x);
} else {
var y = list2[i];
list2[i] = f(x, y);
}
}
}
int square(int x) => x * x;
void main() {
var list1 = <int>[1, 2, 3];
var list2 = new List(3);
map1or2(square, list1, list2);
print(list2);
}
This code is relatively dynamic and avoids lots of types (and in particular
generic arguments to map1or2), but it is a correct strong mode program. In
DDC it prints [1, 4, 9].
There are some implicit dynamics in the program, but we are really
interested in the one explicit dynamic in the function-type test: if (f is
Function(dynamic)). Intuitively, that test looks good: we don't mind which
type the function takes and thus wrote dynamic for the parameter type.
However, that wouldn't work if dynamic was interpreted as Object. In that
case, the is check asks whether the provided f could be invoked with *any*
Object. That's not what we want. We don't want to invoke it with a random
object value that we found somewhere, but invoke it with the values from
list1. It's the caller's responsibility to make sure that the types match.
In fact, we don't care for the type at all. The is check is just here to
test for the arity.
Since checking for arity is a common pattern, strong mode still treats
dynamic as bottom in this context. This function types is thus equivalent
to an arity check.
For a long time, the fuzzy arrow exception was necessary. Dart didn't have
any other way to do arity checking. Only with the move of the Null type to
the bottom of the typing hierarchy, was it possible to explicitly use the
bottom type instead of just dynamic. A sounder way of asking for a
function's arity is thus:
if (f is Function(Null)) {
This can be read as: "is f a function that takes at least null?". Without
non-nullable types *every* 1-arity function takes null and this test is
equivalent to asking whether the function takes one argument.
<footnote> With non-nullable types, the bottom type would need to change,
since there are types that wouldn't accept null anymore. At that point we
would need to introduce a Nothing type, and the is-check would need to be
rewritten to if (f is Function(Nothing)). Admittedly, the spoken
interpretation doesn't sound as logical anymore: "is f a function that
takes at least Nothing?" </footnote>
Since there is now a "correct" way of testing for the arity of functions,
the language team recently started to investigate whether we could drop the
fuzzy arrow exception from strong mode (and thus Dart 2.0).
Although the removal of fuzzy errors leads to breakages, our experience is
pretty positive so far. The biggest problems arise in cases where the
current type system is too weak to provide a correct replacement. Among
those, Map.fromIterablesclearly makes the biggest problems. The old
signature of that constructor is Map.fromIterable(Iterable iterable, {K
key(element), V value(element)}). Implicitly, both functions for key and
value take dynamic arguments and use the fuzzy arrow exceptions to support
iterables of any kind.
Without the fuzzy arrow exception the implicit dynamic in those types is
read similar to Object, thus requiring users to provide functions that can
deal with *any* object (and not just the ones from the iterable).
Unfortunately, our trick of replacing the dynamic with Null doesn't work
here:
Map.fromIterable(Iterable iterable,
{K key(Null element), V value(Null element)}) {
...
}
// Works when the argument is not a function literal:new Map<int,
String>.fromIterable(["1", "2"], keys: int.parse);
// Doesn't work, with function literal:new Map<int,
String>.fromIterable([1, 2], values: (x) => x.toString());
The reason the second instantiation doesn't work is that Dart uses the
context of a function literal to infer the parameter type. In this case the
literal (x) => x.toString() is used in a context where a V Function(Null) is
expected, and the literal is thus automatically adapted to satisfy this
signature: String Function(Null). However, that means that any invocation
of this function with a value that is not null yields to a dynamic error.
The correct way to fix this constructor is to allow generic arguments for
constructors:
Map.fromIterable<T>(Iterable<T> iterable,
{K key(T element), V value(T element)}) {
...
}
Supporting generic arguments for constructors is on our roadmap, but will
not make it for Dart 2.0. In the meantime we either have to live with
requiring functions that take objects, or we will have to change the key
and value type annotation to Function, thus losing the arity and type
information:
Map.fromIterable(Iterable iterable, {Function key, Function value}) {
...
}
<https://github.com/dart-lang/sdk/blob/master/docs/newsletter/20170908.md#enhanced-type-promotion>Enhanced
Type Promotion
As mentioned in a previous newsletter: one of our goals is to improve
Dart's type promotion. We want to make better use of is and is! checks. For
example, promote x to int after the if in the following code: if (x is!
int) throw x;.
When the language team discussed this topic we looked at the conditions
under which type promotion would be useful and intuitive. One of the
current restrictions is that promoted variables may not be assigned again:
void foo(Object x) {
if (x is String) {
x = x.subString(1); // Error: subString is not defined for Object.
print(x + "suffix");
}
}
void bar(Object x) {
if (x is WrappedInt) {
x = x.value; // Error: `value` is not defined for Object.
}
assert(x is int);
}
As can be seen in these two examples, assignments would require an analysis
that deals with flow-control, and that assigns potentially different types
to the same variable. Inside foo the user wants to continue using x as
String, whereas in barthe user wants to use x as an Object after the
assignment.
We have discussed multiple approaches to provide the correct, intuitive
behavior in these cases, and we are confident that we can provide a
solution that will work in most cases. However, we don't want to delay or
block the "easy" improvements, and therefore decided to exclude assignments
from the current proposal. We will come back to assignments of promoted
variables in the future.
--
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.