-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
StreamTransformer is difficult to use in Dart 2.0 #29878
Comments
A workaround is to modify the reified runtime type on the stream:
should do it. |
True, it's annoying, but a direct consequence of Dart's covariant generic classes. A We don't have a way to specify, or detect, that a type parameter of a class occurs only contravariantly and should be treated as such. |
Here's another case we're hitting in Simplified, we end up with something like this: import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
final codec = new AsciiCodec();
Stream<List<int>> read() {
// Reified as a Stream<Uint8List>
var stream = new Stream.fromIterable(<Uint8List>[codec.encode("hello"), codec.encode("world")]);
return stream;
}
void main() {
// This throws: Type 'AsciiDecoder' is not a subtype of type 'StreamTransformer<Uint8List, String>'
codec.decodeStream(read()).then(print);
} |
Is there something actionable here the language/sdk team can do to improve? We know what the problem is. How do we fix? I've heard ideas including language changes or new APIs for this functionality, but not clear if we're proceeding on any front. |
+1 this is a major pain point. Honestly I'd love to see this just become a function definition: typedef StreamTransformer = T Function<S, T>(Stream<S> input); |
The correct solution is to introduce contravariant generics. We currently have no plans to add those. Making |
@floitschG How do you want to proceed? I don't think the status quo is working. |
FWIW I have almost never used abstract class Converter<S, T> {
const factory Converter.fromTransformer(StreamTransformer<S, T> transformer);
} To handle this case. |
E.g., a fairly minimal change: https://codereview.chromium.org/2954383002/diff/1/sdk/lib/async/stream.dart gets this last example running (and perhaps the rest?). I'm not fond of writing |
@matanlurey The problem is not to make a abstract class Converter<S, T> implements StreamTransformer<S, T> { That means that they will have the same co-/contra-variance problems as you are seeing with Even if we make class C<T> {
void call(T x);
}
typedef F = void Function<T>(T);
main() {
var i = new C<int>();
var n = new C<num>();
print(n is C<int>); // false
print(n is F<int>); // true
print(n is C<Object>); // true
print(n is F<Object>); // false
C<Object> o = n;
F<Object> fo = o; // No warning, strong mode runtime error
} So, we can perhaps fix the problem for transformer (if we make it a function type, not a class type), but that won't help |
FWIW I'm very ok with breaking Converter. I don't see it used much outside
of JSON or UTF8 in client code and it seems easy enough to wrap those two
in the core library.
…On Mon, Jun 26, 2017, 10:52 PM Lasse R.H. Nielsen ***@***.***> wrote:
@matanlurey <https://github.com/matanlurey> The problem is not to make a
StreamTransformer into a Converter, but that many transformers *are*
already converters, because all converters are tranformers:
abstract class Converter<S, T> implements StreamTransformer<S, T> {
That means that they will have the same co-/contra-variance problems as
you are seeing with StreamTransformer, it's not just about that class.
Even if we make StreamTransformer into a function type so it's
contravariant in the source type, that won't help Converter. you could
*extract* a transformer from a converter by tearing off the bind method,
but it wouldn't *be* a transformer unless we also add a call method that
forwards to bind, and then you have function subtyping and class
subtyping being at odds with each other. That's not going to be ... at all
understandable.
class C<T> {
void call(T x);
}typedef F = void Function<T>(T);main() {
var i = new C<int>();
var n = new C<num>();
print(n is C<int>); // false
print(n is F<int>); // true
print(n is C<Object>); // true
print(n is F<Object>); // false
C<Object> o = n;
F<Object> fo = o; // No warning, strong mode runtime error
}
So, we can perhaps fix the problem for transformer (if we make it a
function type, not a class type), but that won't help Converter.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#29878 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAKQ7jv7xkCTU_Em9P9650kwSSsT0p4_ks5sIJiAgaJpZM4N6jMu>
.
|
@munificent @lrhn
|
Definitely not a P1. I think this is worth revisiting once we ship the variance changes. |
Agree. There isn't much we can do with the current API and feature-set. |
This has come up several times, but I don't see a bug for it.
Here's a sample use that highlights the problem:
The
change
method looks just fine from a typing perspective. However, if we invoke it withStream<T>
whereT
is a proper subtype ofIterable
, it will trigger a confusing runtime error:Intuitively,
StreamTransformer
should be contravariant on it's first type parameter (Iterable
here). But, Dart does not support that.The text was updated successfully, but these errors were encountered: