You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
streams: Add support for non static compose #43664
Let's say the first transformer is parsing binary file.
Binary files are usually made out of blocks let's take for example the pcapng file format (Packet capture format that contains a "dump" of data packets captured over a network) as I'm very familiar with it:
Pcapng file made out of blocks, each block has the total size in it as the size can change depending on the block body
@mcollina I think a good motivating example of something you can easily do with compose but not any of the other operators (or more accurately - I haven't found a way but maybe you know one) is readline.
Consider this pseudocode for a readline implementation
The code can be made shorter but my point wasn't brevity but to underline how this fundamentally needs to keep state between iterations.
For .map to be able to do this it'd need a "scope" it can reference between loop iterations easily e.g.:
asyncfunctionreadlines(stream){letchunks=[];// captured by closurereturnfs.createReadStream('./someFile').map(function(chunk){constnewLine=chunk.indexOf('\n')if(newLine!==-1){yieldBuffer.concat(chunksconcat(chunk.subarray(0,newLine);chunks=[chunk.subarray(newLine)];}else{
chunks.push(chunk);}});}
Now this technically works (unlike for example RxJS) because iterator helpers are on the iterator and not the iterable so the function is guaranteed to only run for one stream instance (which is actually very fortunate because this is a common footgun in .NET land) - but it's also less composable (the .map code is not reusable unlike the compose code) and relies on the function/block + a closure for scope.
.scan is like an iterative reduce with state. It's less powerful than .compose (name pending) like the static compose but I do think it can accommodate the use case mentioned here.
That said I'd personally like to see more community interest before I'd feel we should do this?
Activity
rluvaton commentedon Jul 3, 2022
Cc @benjamingr
benjamingr commentedon Jul 3, 2022
@nodejs/streams
mcollina commentedon Jul 3, 2022
What use cases would this solve vs the static compose?
rluvaton commentedon Jul 3, 2022
no real problem but make the code much more readable and consistent with the chaining API
Let's say you have a transformer at the start and at the middle, it will be really hard to read:
Example using static
compose:And using it as a non-static method:
mcollina commentedon Jul 4, 2022
I think this use case is already covered by
.map():I'm not sure the syntax difference is worth adding another method on
Readable. However, I'm happy to hear what other thinks.rluvaton commentedon Jul 4, 2022
Map only map 1 input to 1 output and there are cases where we want to map 1/many input to many/1 output
rluvaton commentedon Jul 4, 2022
Let's say the first transformer is parsing binary file.
Binary files are usually made out of blocks let's take for example the pcapng file format (Packet capture format that contains a "dump" of data packets captured over a network) as I'm very familiar with it:
Pcapng file made out of blocks, each block has the total size in it as the size can change depending on the block body
Using map we can't split the file to multiple blocks and parse each one individually.
benjamingr commentedon Jul 4, 2022
@mcollina I think a good motivating example of something you can easily do with
composebut not any of the other operators (or more accurately - I haven't found a way but maybe you know one) isreadline.Consider this pseudocode for a readline implementation
The code can be made shorter but my point wasn't brevity but to underline how this fundamentally needs to keep state between iterations.
For
.mapto be able to do this it'd need a "scope" it can reference between loop iterations easily e.g.:Now this technically works (unlike for example RxJS) because iterator helpers are on the iterator and not the iterable so the function is guaranteed to only run for one stream instance (which is actually very fortunate because this is a common footgun in .NET land) - but it's also less composable (the
.mapcode is not reusable unlike the compose code) and relies on the function/block + a closure for scope.ronag commentedon Jul 4, 2022
.scan?
rluvaton commentedon Jul 4, 2022
I'm assuming you meant the RxJS scan method, I'm not very familiar with it but isn't it like reduce?
ronag commentedon Jul 6, 2022
No, they are different.
The upside of a compose is that one can have resource management inside the function, which is not possible with any of the other operators.
ronag commentedon Jul 6, 2022
@rluvaton Would you mind opening a PR for .compose? Not promising we'll merge it, but I think it's worth further consideration.
benjamingr commentedon Jul 6, 2022
.scanis like an iterative reduce with state. It's less powerful than.compose(name pending) like the static compose but I do think it can accommodate the use case mentioned here.That said I'd personally like to see more community interest before I'd feel we should do this?