Yesterday finally I find some time to work on my own HTTP client written in Swift (LightSessionKit) using Swift brand new support for asynchronous functions with async / await and I immediately stumbled across a problem: It’s not possible to use reduce API within a collection that conforms to the Sequence protocol. (although it’s possible to use with AsyncSequence).

I did a quick google search and I found this library by John Sundell which adds asynchronoussupport for most Sequence functions such as map, flatMap, compactMap and forEach but not for reduce so let’s go ahead and add support ourselves.

But before doing so, let’s understand what reduce does and how it works. According to the official Apple documentation this is what reduce does:

Returns the result of combining the elements of the sequence using the given closure.

Easy right?, for a given sequence of elements it returns a unique element starting from a initial value and applying a closure to all elements of the sequence. Taking a look at the original method signature it takes two values, an initial value and a closure.

  • initialResult: our initial accumulating value (this will be the same for an asynchronous variant).
  • nextPartialResult: According to the official documentation, a closure that combines an accumulating value and an element of the sequence into a new accumulating value. In our case, signature will be a bit different as it has to be asynchronous.
  • Return Value: As closures results are cascading down we will also need to add the async keyword here.

    This will be our final signature:

func asyncReduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult: ((Result, Element) async throws -> Result)
) async rethrows -> Result {
    // TODO: Implementation goes here
}

Now let’s get our hands dirty and do our own implementation for an asynchronous reduce. We just need to start with initialResult as starting point, then loop over all elements of the Sequence and execute the given closure with initialResult and all elements inside. This will be the final implementation:

public extension Sequence {
    func asyncReduce<Result>(
        _ initialResult: Result,
        _ nextPartialResult: ((Result, Element) async throws -> Result)
    ) async rethrows -> Result {
        var result = initialResult
        for element in self {
            result = try await nextPartialResult(result, element)
        }
        return result
    }
}

And that’s it, full implementation is here.

PD: Bear in mind that although this will allow to execute asynchronous closures with reduce, these closures will perform each of their operations in sequence, one after the other.