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
So the result depends on the time of calling the scan.
I would like to run the same example with Hareactive, but at the moment it is not quite clear to me what would be the best way. From the scan tests I see that a sinkStream is used to create a "ProducerStream", then scan is followed the .at() evaluation, and then by subscribe, whose role is not quite clear to me, in particular, whether it turns stream from pending into active, like other libraries do. Then events need to be published. I wonder if there were any more direct way similar to the above.
It can be seen as composition of truncating the stream events between two moments into array (forgetting the times) and subsequent reduce. The latter part is pure. The impurity comes from the implicit dependence on the first moment (the moment when the scan was called). It is still pure in the second moment, which is the current time.
The scan becomes pure when applied to the so-called "cold" (I find "pending" more descriptive) streams, the ones that had not received any value yet. This is how they do it in MostJS. Any of their method "activating" the stream, transforms it into Promise, after which methods like scan are not allowed. That way scan remains pure.
The 2 scan methods here are pure, however, they differ from other libraries in which they return the more complex types of either Behavior<Stream<A>> or Behavior<Behavior<A>>.
The implementation (as always) varies among libraries:
flyd (and mithril/stream) allows scan on any stream and returns stream
MostJS scan regards all Streams as "cold", with the additional mosj-subject to use with active streams, however, the purity is lost in that case.
Xstream does not have scan, it seems to be replaced with the fold which "Combines events from the past throughout the entire execution of the input stream". That seems to solve the purity problem for them, but may not be as convenient to use.
KefirJS https://rpominov.github.io/kefir/#scan and BaconJS https://baconjs.github.io/api.html let their scan to transform stream into
what they call "property", which I understand is the same as Behavior here.
I am not familiar with details but they seem to talk about "active", so possibly they way is similar to MostJS.
The RxJS makes the seed argument optional. Which, however, presents problems if it is of different type than the accumulator. (They only demonstrate the simple addition case, where the types are equal.)
The same is in KefirJS
Possible suggestions:
Change the name to something like pureScan to emphasise both the difference and the motivation for the additional complexity, and to avoid the confusion with the other scans.
I would like to have some safe and simple variant. Like stream and behavior in one thing, what Xstream calls the Memory Stream. So I know that both stream and behavior would conform to this memoryStream interface and I don't have to think which one is which. It may be derived from other more refined versions, but I would like to have it for the sake of convenience.
A new MemoryStream interface might be a great entry point to build adapters for other libraries as it would accept all streams, promises, behaviours, properties and even observables. So people can use their old code with the api they understand, which is great.
A new Pending interface could be combined with Streams, Behaviors, or MemoryStreams. It would allow the use the "unlifted" version of scan, while preserving the purity.
The scan function for the Pending interface could be called something like "pendingScan" to emphasise its use case. It would only apply to pending memory streams, in its pure form. Its impure brother can be called "impureScan" and would apply to any memory stream, but with "impure" in it's name, it is no more the library's responsibility :)
The reducer would gets called and the initialisation time (when the stream is not pending) and when the events are emitted.
The
scanfunction seems to be both common and useful,but it also caused me some confusions like here and here that I'd like to clear if possible.
Here is my current understanding (appreciate any correction):
scanfunction inflydis impure, strictly speaking. The simplest possible example isSo the result depends on the time of calling the
scan.I would like to run the same example with
Hareactive, but at the moment it is not quite clear to me what would be the best way. From the scan tests I see that asinkStreamis used to create a "ProducerStream", thenscanis followed the.at()evaluation, and then bysubscribe, whose role is not quite clear to me, in particular, whether it turns stream from pending into active, like other libraries do. Then events need to be published. I wonder if there were any more direct way similar to the above.It can be seen as composition of truncating the stream events between two moments into array (forgetting the times) and subsequent
reduce. The latter part is pure. The impurity comes from the implicit dependence on the first moment (the moment when thescanwas called). It is still pure in the second moment, which is the current time.The
scanbecomes pure when applied to the so-called "cold" (I find "pending" more descriptive) streams, the ones that had not received any value yet. This is how they do it in MostJS. Any of their method "activating" the stream, transforms it into Promise, after which methods likescanare not allowed. That wayscanremains pure.Applying
scanonly to the pending streams is the most common use case, as e.g. @JAForbes was suggesting in Allow optional seed in Stream.scan? MithrilJS/mithril.js#1822 . Such as the action stream is passed toscanbefore at the initialisation time, whereas the actions begin to arrive after. This fact is also confirmed by the absence of tests in the non-pending cases, for instance, note how in https://github.com/paldepind/flyd/blob/master/test/index.js#L426 the stream is always created empty.The 2
scanmethods here are pure, however, they differ from other libraries in which they return the more complex types of eitherBehavior<Stream<A>>orBehavior<Behavior<A>>.The implementation (as always) varies among libraries:
flyd(andmithril/stream) allowsscanon any stream and returns streamMostJS
scanregards all Streams as "cold", with the additionalmosj-subjectto use with active streams, however, the purity is lost in that case.Xstream does not have
scan, it seems to be replaced with thefoldwhich "Combines events from the past throughout the entire execution of the input stream". That seems to solve the purity problem for them, but may not be as convenient to use.KefirJS https://rpominov.github.io/kefir/#scan and BaconJS https://baconjs.github.io/api.html let their
scanto transform stream intowhat they call "property", which I understand is the same as Behavior here.
I am not familiar with details but they seem to talk about "active", so possibly they way is similar to MostJS.
The
RxJSmakes theseedargument optional. Which, however, presents problems if it is of different type than the accumulator. (They only demonstrate the simple addition case, where the types are equal.)The same is in KefirJS
Possible suggestions:
Change the name to something like
pureScanto emphasise both the difference and the motivation for the additional complexity, and to avoid the confusion with the otherscans.I would like to have some safe and simple variant. Like stream and behavior in one thing, what Xstream calls the Memory Stream. So I know that both stream and behavior would conform to this
memoryStreaminterface and I don't have to think which one is which. It may be derived from other more refined versions, but I would like to have it for the sake of convenience.A new
MemoryStreaminterface might be a great entry point to build adapters for other libraries as it would accept all streams, promises, behaviours, properties and even observables. So people can use their old code with the api they understand, which is great.A new
Pendinginterface could be combined with Streams, Behaviors, or MemoryStreams. It would allow the use the "unlifted" version ofscan, while preserving the purity.The
scanfunction for thePendinginterface could be called something like "pendingScan" to emphasise its use case. It would only apply to pending memory streams, in its pure form. Its impure brother can be called "impureScan" and would apply to any memory stream, but with "impure" in it's name, it is no more the library's responsibility :)The reducer would gets called and the initialisation time (when the stream is not pending) and when the events are emitted.
Let me know what you think.