The method is like
Sequence.reduce, but instead of returning just the result after the last source element, we prepend it with all the intermediate reductions. The scan may be exclusive, which starts with a seed value like
reduce (resulting in
n + 1 elements), or inclusive, which starts with the first element (resulting in
n elements). (Those result counts assume we dump the seed/first directly into the result sequence; if we don’t then the results will be only
n - 1 elements long, respectively.)
When the combining closure is throwing, we would do the usual returning of an
Array (or an overload that lets us choose the range-replaceable collection type) of the results. With a non-throwing closure, we can make a separate lazy result object. The result sequence should be infinite if the source is infinite. It should be a (forward-only) collection if the source is a collection.
This idea has been brought up before, and once reviewed but rejected.
Why am I bringing it up again, now? I just found out that Apple snuck in a version of
scan as part of their WWDC 2019 SwiftUI/Combine New World Order. If they thought it was useful enough to be sick & tired of waiting on us to work on it, maybe we should consider adding it to the general Swift capability set.
The downside is that we’re committed to the “
scan” name. Yes, it is the term of art, but that fact is completely incomprehensible to those who are not functional-programming nerds. It’s not hard to understand “reduce, but including the sequence of intermediate results” for regular programmers, but getting “scan” from that is.
It still looks (to me) much the same that “its use case is narrow”.
A stronger argument would be when multiple libraries start to ship their own
Could you also include a link to documentation of
scan? It’s a little tricky to find with the word being so common.
I always thought "reduce" was an odd term, so I'm sure "scan" won't bother me any more than that ;)
scan that's provided by Combine is mostly useful in an asynchronous environment where new values are published over (a sometimes very long) time so you want to get a "partial" reduce (scan) of the values that have been published up until "now".
I think that use case is far more common and useful than looping over a known collection, essentially reducing it, and having all intermediate values available to you.
scan isn't really relevant, different use case as @donny_wals suggests. But my view is it's a good addition to the standard library and should be re-proposed. It's got lots of valid use cases (running totals for example).
At the last proposal,
reductions got positive feedback. But
scan is enough of a term of art that it'd be fine too.
Agreed. I've wanted it a couple times in the last few months.
I've actually wanted both
scan and "reverse scan" (i.e.
a.reversed().scan( ... ).reversed(), but computed directly). Not sure that it makes sense to bundle that into the proposal however, as it's rather more niche.
After a quick thought, I don’t think there’s a wrapper function or type that would be more optimal that the direct code you wrote.
Sure, but it's quite verbose and not immediately obvious to the reader what it's doing; it's actually--for me--more compelling than
scan, which has a pretty obvious spelling as