I've seen that a lot. And it works, a lot, because there's often an identity value to work with, like "", 0, or 1.
What about the Bool case though? I usually just see people copy a portion of a string , or use an inline closure that matches the extension method above.
I guess the reason I never thought of Bool.reduce is because it's not super common to have the "false" case be a subset of the "true" case, and also because there's not an obvious reason to privilege "true" over "false". The Optional case comes up more but I usually try to go for @cukr's solution instead, leaving the cases where that doesn't work rare enough that I don't feel the need to define a helper.
(I also personally wouldn't pick the name reduce for the Bool case because it's not removing any wrapping layer, but I see the parallel in the "how many times is the function applied".)
There's no reduce specifically defined on CollectionOfOne; it's fallout from being a Collection (technically Sequence). We certainly wouldn't add it if it weren't present, since it is useless. But the general question stands: why have, say, map on both Collections and Optionals, but not reduce?
I think a big part of the answer is that it really isn't a common operation if you're not trying to chain everything in one expression, and even then, it's only useful if your "nil" value is relevant to your "non-nil" value, which I think is pretty uncommon. The fully general pattern I'd use for this is foo.map { 1 + $0 } ?? 1 (i.e. your implementation). Is foo.reduce(1, +) simpler than that? Well, yes, but it's also more restrictive—it comes out worse for foo.map { 1 + $0 } ?? 0. (Specifically, foo.reduce(0) { $1 + 1 }) That certainly doesn't mean "reduce shouldn't be added", but if it's not going to be the right tool for the job very often, does it belong in the standard library?
…But on the other hand, what harm would it do?
FWIW, the only other thread I've found on this is one you participated in: Add Optional.filter to the Standard Library - #41 by gwendal.roue. I'm personally of the opinion that (a) Optional is not a Collection, but also that (b) that shouldn't stop us from adding the Collection-like operations (monadic, foldable, whatever) that make sense on Optional.*
* Whether there should be, say, a Foldable protocol is a separate issue that unfortunately gets into binary compatibility problems.
I wish it were! But it's not, because closures can't be applied to values, using dot syntax, unless they're Sequences or Optionals. So people come up with stuff like this:
…which is more easily served by:
public extension Sequence {
/// `reduce`, disregarding all sequence elements.
@inlinable func reduce<Result>(
_ initialResult: Result,
_ nextPartialResult: (_ partialResult: Result) throws -> Result
) rethrows -> Result {
try reduce(initialResult) { partialResult, _ in
try nextPartialResult(partialResult)
}
}
/// `reduce`, disregarding all sequence elements.
@inlinable func reduce<Result>(
into initialResult: Result,
_ updateAccumulatingResult: (_ partialResult: inout Result) throws -> Void
) rethrows -> Result {
try reduce(into: initialResult) { partialResult, _ in
try updateAccumulatingResult(&partialResult)
}
}
…along with:
public extension CollectionOfOne where Element == Never? {
/// A collection whose element is irrelevant.
init() {
self.init(nil)
}
}
I mean, yes, this is the hidden premise behind a lot of these threads. Maybe the answer is "there should be a way to put methods on every type (extension Any)" and maybe the answer is "yeah, you don't need to put everything in one expression". In either case, though, Optional and CollectionOfOne aren't intended to be the answer to this problem; either we should have a way to put methods on every type or we shouldn't.