Pitch: Reducing a sequence onto its own elements to simplify code and reduce errors

@Erica_Sadun This is an awesome proposal I read through the gist and saw what it means to fold. But I would rather leave it out of the standard library because of the nil. Swift is already a language forcing us to avoid nil. So if this is introduced you will have to start dealing with nil again with another ´guard let´ which is not funny. The initialResult is actually a good practice. So would keep it as it is. However anyone interested in fold and can just copy the code into and extension for sequence. This is my one cent opinion.

The nil initially bothered me. It does not now.

One often treats an empty list as either a special case or uses a fallback value. For example, think about a list followed by a count. "Cart subtotal: $49.92" versus "Your shopping cart is empty". A conditional binding is a natural way of expressing these two as different outcomes:

if let subtotal = items.map({ $0.cost }).reduce(+) {
    return "\(localized: "Cart subtotal"): \(localizedCurrency: subtotal)"
} else {
    return "\(localized: "Your shopping cart is empty")"
}

The more I use this in actual code, the happier I am with the design. The alternate is to check for the identity and special case that and I think it's uglier.

10 Likes

The performance increase is real and the extra API is mostly invisible to the user. We often work to please the novice. This addition supports the proficient.

5 Likes

Modified version

extension Sequence {
    
    public func reduce(_ nextPartialResult: (_ partialResult: Element, _ current: Element) throws -> Element) rethrows -> Element? {
        return try self.reduce(nil) { partial, current in try partial.map { try nextPartialResult($0, current) } ?? current }
    }
}

this one is ok.

var counter = 0

struct MySequence : Sequence, IteratorProtocol {
    
    func next() -> Int? {
        counter += 1
        return counter
    }
}

let sequence = MySequence()

sequence.prefix(5).reduce(+)     // 15
Array(sequence.prefix(5))     // [6, 7, 8, 9, 10]