Reconsider adding “scan” (i.e. progressively reduce; a.k.a. iota, partial-sum, reductions, accumulate)

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 and 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.

6 Likes

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 scan.

PS

Could you also include a link to documentation of scan? It’s a little tricky to find with the word being so common.

1 Like

I always thought "reduce" was an odd term, so I'm sure "scan" won't bother me any more than that ;)

This is the scan the author was thinking of: https://developer.apple.com/documentation/combine/publishers/scan

1 Like

The 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.

The Combine 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.

6 Likes

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 reduce(into:).

2 Likes