'Leaf Petersen' via Dart Misc
2018-02-21 22:14:08 UTC
Hi Dart folks -
We (the Dart team) are preparing to land a breaking change for Dart 2
related to callable classes. Below is a summary of the changes and how
they might impact you. Feel free to follow up here or with me directly
with questions.
*What is changing?*
In Dart 1, a class with a call method is a subtype of a function type with
the same signature.
```dart
// Dart 1
class C {
void call(int x) {}
}
typedef void F(int i);
void main() {
C c = new C<int>();
F f = c; // OK
f as C; // OK, cast succeeds
c(3); // OK
print(f is C); // OK, prints true.
F f2 = c;
print(identical(f, c)); // OK, prints true
print(f == c); // OK, prints true
print(identical(f, f2)); // OK, prints true
print(f == f2); // OK, prints true
}
```
In Dart 2, we will continue to allow objects with call methods to be
called, and to be assigned to variables of function type. However, the
assignment of an object with a call method to a function type will now be
treated as an implicit closurization of the `.call` method of the object.
This changes the behavior of the code snippet above as follows:
```dart
// Dart 2
class C {
void call(int x) {}
}
typedef void F(int i);
void main() {
C c = new C<int>();
F f = c; // OK, treated as F f = c.call;
f as C; // Cast fails
c(3); // OK
print(f is C); // OK, prints false.
F f2 = c;
print(identical(f, c)); // OK, prints false
print(f == c); // OK, prints false
print(identical(f, f2)); // OK, prints true or false
print(f == f2); // OK, prints true
}
```
*What will break?*
Most code that uses call methods is unaffected by this, and will silently
continue to work.
Code that passes off callable objects as functions and then casts them back
to the original class type will no longer work. This is rare, but code
such as this can be refactored either to use the Expando class to attach
the required state to an actual function, or to explicitly manage state
using Maps from the function to associated state.
Code that relies on a callable object being identical to the result of
implicitly casting the callable object to a function type will no longer
work. This is rare, but code that relies on this will need to be
refactored.
*How do I know if this has broken me?*
The most likely error that you will likely see as a result of this change
is a runtime cast failure, with a message that looks like this:
Type '(int) => int' is not a subtype of type 'CallableClass'
This error is indicating that a function typed value is being explicitly or
implicitly cast to `CallableClass`, where `CallableClass` is some class
with a `call` method. Previously, the value being cast would have been an
instance of `CallableClass`, and so the cast would have succeeded. After
this change, the value being cast is the closurization of the `.call`
method from `CallableClass`, and cannot be cast back to the original
object.
*Why is this change being made?*
Callable classes cause complexity in the type system (e.g.
https://github.com/dart-lang/sdk/issues/29791), and also cause substantial
implementation complexity. Making uses of callable classes as function
types behave as a closurization of the call method supports the vast
majority of use cases that we have found, at a tiny fraction of the
implementation complexity.
I will update here when this change lands. Please reach out to me here or
offline with any concerns, and/or with help resolving any issues after this
change has landed.
thanks,
-leaf
We (the Dart team) are preparing to land a breaking change for Dart 2
related to callable classes. Below is a summary of the changes and how
they might impact you. Feel free to follow up here or with me directly
with questions.
*What is changing?*
In Dart 1, a class with a call method is a subtype of a function type with
the same signature.
```dart
// Dart 1
class C {
void call(int x) {}
}
typedef void F(int i);
void main() {
C c = new C<int>();
F f = c; // OK
f as C; // OK, cast succeeds
c(3); // OK
print(f is C); // OK, prints true.
F f2 = c;
print(identical(f, c)); // OK, prints true
print(f == c); // OK, prints true
print(identical(f, f2)); // OK, prints true
print(f == f2); // OK, prints true
}
```
In Dart 2, we will continue to allow objects with call methods to be
called, and to be assigned to variables of function type. However, the
assignment of an object with a call method to a function type will now be
treated as an implicit closurization of the `.call` method of the object.
This changes the behavior of the code snippet above as follows:
```dart
// Dart 2
class C {
void call(int x) {}
}
typedef void F(int i);
void main() {
C c = new C<int>();
F f = c; // OK, treated as F f = c.call;
f as C; // Cast fails
c(3); // OK
print(f is C); // OK, prints false.
F f2 = c;
print(identical(f, c)); // OK, prints false
print(f == c); // OK, prints false
print(identical(f, f2)); // OK, prints true or false
print(f == f2); // OK, prints true
}
```
*What will break?*
Most code that uses call methods is unaffected by this, and will silently
continue to work.
Code that passes off callable objects as functions and then casts them back
to the original class type will no longer work. This is rare, but code
such as this can be refactored either to use the Expando class to attach
the required state to an actual function, or to explicitly manage state
using Maps from the function to associated state.
Code that relies on a callable object being identical to the result of
implicitly casting the callable object to a function type will no longer
work. This is rare, but code that relies on this will need to be
refactored.
*How do I know if this has broken me?*
The most likely error that you will likely see as a result of this change
is a runtime cast failure, with a message that looks like this:
Type '(int) => int' is not a subtype of type 'CallableClass'
This error is indicating that a function typed value is being explicitly or
implicitly cast to `CallableClass`, where `CallableClass` is some class
with a `call` method. Previously, the value being cast would have been an
instance of `CallableClass`, and so the cast would have succeeded. After
this change, the value being cast is the closurization of the `.call`
method from `CallableClass`, and cannot be cast back to the original
object.
*Why is this change being made?*
Callable classes cause complexity in the type system (e.g.
https://github.com/dart-lang/sdk/issues/29791), and also cause substantial
implementation complexity. Making uses of callable classes as function
types behave as a closurization of the call method supports the vast
majority of use cases that we have found, at a tiny fraction of the
implementation complexity.
I will update here when this change lands. Please reach out to me here or
offline with any concerns, and/or with help resolving any issues after this
change has landed.
thanks,
-leaf
--
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.