-
Notifications
You must be signed in to change notification settings - Fork 7
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 MappingIterator for synchronous transformations #59
Conversation
@RubenVerborgh When I run it locally node 10 coveralls check is complaining about a comment in line 38 - so not sure how to resolve that one... |
Rather than |
This is ready for review @RubenVerborgh - given that this is now much cleaner than #58 I suggest that we merge the branch more-or-less in this state and worry about possible performance tweaks in the order of 10-20% at a later date. |
Excellent. Thanks, @jeswr; I can work with this! |
b5f2dd0
to
6b4f039
Compare
6b4f039
to
34e1a60
Compare
7beffc0
to
4652546
Compare
4652546
to
38ae72f
Compare
095b446
to
fd69434
Compare
@jeswr Thanks for your patience. Gave this a deep review. This is almost good to go, except the As a fun challenge (and looping in @joachimvh), I gave up on the typing of |
private readonly _mappingRoot: InternalSource<any>; | ||
|
||
// This is wrong: readable should be set by listening to source events | ||
get readable() { |
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 breaks things, because certain readable
events will not be fired. We need to actually set readable = true
.
fd69434
to
e8b1a7c
Compare
@@ -25,6 +25,11 @@ export function setTaskScheduler(scheduler: TaskScheduler): void { | |||
taskScheduler = scheduler; | |||
} | |||
|
|||
/** Binds a function to an object */ | |||
function bind(fn: Function, self?: 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.
function bind(fn: Function, self?: object) { | |
function bind<T extends Function>(fn: T, self?: object): T { |
is the correct one, but that leads to errors below.
} | ||
|
||
map<K>(map: (item: D, it: AsyncIterator<any>) => K | null, self?: any): AsyncIterator<K> { | ||
return new MappingIterator<S, K>(this._source, [...this._mappings, bind(map, self)], this); |
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.
Something is off here.
MappingIterator
(overload 2) takes as constructor parameters the direct source (should be this
), the mappings, and the mapping root (should be this._mappingRoot
). Yet the tests do not catch this problem.
e8b1a7c
to
3be4178
Compare
let mapped : any = null; | ||
while (mapped === null && (mapped = this._source.read()) !== null) { | ||
for (let i = 0; i < this._mappings.length; i++) { | ||
mapped = this._mappings[i](mapped, this); |
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 this
pointer is not correct here; that should be the MappingIterator
at each level.
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 you are right but I am still context switching back to this issue so not fully confident yet.
Sorry for the haphazard comments, @jeswr! They may look random, but it's actually three separate waves of working on this, and some crucial thinking work in between. However, this is the conclusion I've come to:
I know some of the above go against my earlier suggestions, but reviewing the resulting code in depth brought new insights. |
|
||
read(): D | null { | ||
let mapped : any = null; | ||
while (mapped === null && (mapped = this._source.read()) !== null) { |
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.
Let's not read the source if we are closed. Need to guard for this. And test.
I haven't fully context switched back to this issue so apologies if any of these q's/notes are obvious or obviously incorrect. For reference here is the original state of the PR when it was opened which I will also refer to a few times below. Firstly I just wanted to make a clarification that I suspect answers this comment. The The original PR also did not contain constructor overloads so I'll need a little more time to understand what has been done there before I can comment on whether there is indeed a bug now.
I'm still not convinced that this was a problem when I started this PR. The logic flow I was following is that when This ensures that Since the As for the issue around
What kind of optimisations does this exclude? Importantly, can they be achieved if we just pass a |
Yeah, sorry that I had to leave this one for a while. I think it's the last thing I worked on before I caught covid, so I've never been able to complete my original review. Back on things now though!
The only thing this does is allowing people to pass in a single mapping function, so it not needing to be a one-element array. The array constructor is considered private, given that it is an internal optimization; so I essentially split the two cases (external versus internal construction).
Right. My memory is that I fixed the The main challenge with AsyncIterator is correctness; it's sometimes quite hard to reason about edge cases. All tests might work, and suddenly 1 out of a 100 times a 1 million stream will stall and it will be impossible to figure out why. So that's why I really suggest sticking to the existing
Yes, but having the setter might give rise to a whole other range of problems. The I think if you put the setter back, you will see test failures.
Whether we pass an iterator or a
Indeed, but at a cost. Perhaps it does make sense to have an The main question is how we want to approach this PR now, I realize it's very inconvenient that, due to circumstances, there were 2 months between your and my initial work on this, and today. It's not easy to get the context back from then. Do you still have the bandwidth to work on this? Just because you were volunteering back then, does not mean you still have the time today, and I want to be respectful of your contributions. A way forward that I see at the moment is, given the current unit tests (which are a great harness to have), to re-envision the implementation a bit. I.e., if this were myself working on it, I would start with the existing code from the Let me know what you think and what's feasible for you. In any case, thanks a lot for working on this. |
Yep - I can have a go today branching off
No worries! I always enjoy working on OS code and I've learned a lot from the work and reviews. Thanks for all your work on the reviews! |
You're absolutely nailing it in #75, so let's finish things there 👍👍 |
An alternative to #58 which uses callbacks to close the iterator rather than creating a new class.