-
Notifications
You must be signed in to change notification settings - Fork 21
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
Add whereType transformer #60
Conversation
This needs to be implemented as it's own class instead of using one of the helpers like `fromHandlers` because it has unique generic type requirements. We want to avoid requring the call site to re-specify the generic type of the input stream. The return type needs to be a `StreamTransformer<Null, R>` to allow to satisfy static checking. However the argument to `bind` at runtime will never be a `Stream<Null>` so we need to define it explicitly to widen the allowed argument.
/// Errors from the source stream are forwarded directly to the result stream. | ||
StreamTransformer<Null, R> whereType<R>() => _WhereType<R>(); | ||
|
||
class _WhereType<R> extends StreamTransformerBase<Null, R> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The null is a code smell here, what does it represent? can it also be generic, or should it maybe be Object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see you commented about this, I don't fully understand the issue here but I can check out the repo and try it out which would probably make it more obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See some discussion here:
dart-lang/language#180 (comment)
concrete scenario:
var numbers = Stream<num>.fromIterable([1, 1.5, 2]);
var ints = numbers.transform(whereType<int>());
In this case the transform
excepts an argument assignable to StreamTransformer<num, S>
. Since the whereType
call doesn't have a way to statically know the num
there, it either needs us to be explicit about it (numbers.transform(whereType<num, int>()
), or to use some default that can fill in for any T
that may have been on the stream. Null
is the only possibility there for now, I'm not sure if we've figured out what the user referenceable bottom type will be after NNBD...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would at least leave a lengthy doc comment here, its going to be pretty bizarre for anybody reading this in the future so some context would certainly help :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a note to the doc comment, how does that look to you? I think for further details the git blame
should point here which will have extra detail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what you added is fine
lib/src/where_type.dart
Outdated
|
||
class _WhereType<R> extends StreamTransformerBase<Null, R> { | ||
@override | ||
Stream<R> bind(Stream<Object> values) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe inputs
instead of values? values is fairly ambiguous
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This matches the naming pattern used elsewhere in the package.
Stream<T> bind(Stream<S> values) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think its actually a good name though?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's about as good as anything else. A value
or event
is what is emitted by a stream when we don't know anything more specific about it, and I generally like to name a stream as the plural of whatever flows through it. I don't like the name inputs
because I wouldn't call the thing emitted by the stream an input
- I wouldn't name the argument to the listen callback that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the name
inputs
because I wouldn't call the thing emitted by the stream aninput
In the case of a stream transformer I would disagree - it is transforming one stream into another stream, so it makes sense to me to consider one the "inputs" stream and one the "outputs" stream.
If I see the word "values" in the middle of the method its not clear what that represents. Is that the outputs or the inputs? But if I see "inputs" I can immediately assume its the input stream.
In any case I don't care enough to withhold an lgtm, but I do think "inputs" is better :).
I wouldn't name the argument to the listen callback that.
Right, because listen doesn't transform anything, it only listens for values and there is only one stream in scope. With a transformer there are two streams in the scope of the bind
method.
lib/src/where_type.dart
Outdated
controller.onCancel = () { | ||
var toCancel = subscription; | ||
subscription = null; | ||
if (!valuesDone) return toCancel.cancel(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like it would be cleaner to null out the subscription
field when the values are done, and get rid of this valuesDone
field entirely.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I was worried about the onListen
getting called again in a broadcast stream after the values where already done and the subscription != null
condition not blocking us from doing work we shouldn't - but it looks like maybe StreamController
handles this for us and won't call onListen
again anyway. Done
As a more general comment, stream does already have a |
That doesn't end up with the correct static or runtime type. You either need to chain a |
/// Errors from the source stream are forwarded directly to the result stream. | ||
StreamTransformer<Null, R> whereType<R>() => _WhereType<R>(); | ||
|
||
class _WhereType<R> extends StreamTransformerBase<Null, R> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what you added is fine
lib/src/where_type.dart
Outdated
|
||
class _WhereType<R> extends StreamTransformerBase<Null, R> { | ||
@override | ||
Stream<R> bind(Stream<Object> values) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like the name
inputs
because I wouldn't call the thing emitted by the stream aninput
In the case of a stream transformer I would disagree - it is transforming one stream into another stream, so it makes sense to me to consider one the "inputs" stream and one the "outputs" stream.
If I see the word "values" in the middle of the method its not clear what that represents. Is that the outputs or the inputs? But if I see "inputs" I can immediately assume its the input stream.
In any case I don't care enough to withhold an lgtm, but I do think "inputs" is better :).
I wouldn't name the argument to the listen callback that.
Right, because listen doesn't transform anything, it only listens for values and there is only one stream in scope. With a transformer there are two streams in the scope of the bind
method.
controller.close(); | ||
}); | ||
if (!values.isBroadcast) { | ||
controller.onPause = subscription.pause; | ||
controller.onResume = subscription.resume; | ||
} | ||
controller.onCancel = () { | ||
var toCancel = subscription; | ||
subscription?.cancel(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this used to return this value, not sure if it matters or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm not sure either. There was some weirdness with an analyzer hint around this at one point that could have been why I had it in the fromHandlers
implementation, and I copied most of that here.
This needs to be implemented as it's own class instead of using
one of the helpers like
fromHandlers
because it has unique generictype requirements. We want to avoid requring the call site to re-specify
the generic type of the input stream. The return type needs to be a
StreamTransformer<Null, R>
to allow to satisfy static checking.However the argument to
bind
at runtime will never be aStream<Null>
so we need to define it explicitly to widen the allowed argument.