Why doesn't "reduce(into:)" include a variant that takes an "inout" input?


#1

After seeing the "Reducing a sequence onto its own elements to simplify code and reduce errors" pitch, I was considering that it should have an inout variant, and that got me to wondering why reduce(into:) didn't have an additional variant that mutated its initial value.

I found @dabrahams' comment from the original review:

But I ran some naive benchmarks and found that this kind of code can still require copies depending on the value being accumulated.

  func testRawAccumulation() {
    let ys = Array(repeating: 1, count: 1_000_000)
    var xs: [Int] = []
    xs.reserveCapacity(5_000_000)
    measure {
      for y in ys { xs.append(y) }
      for y in ys { xs.append(y) }
      for y in ys { xs.append(y) }
      for y in ys { xs.append(y) }
      for y in ys { xs.append(y) }
    }
  }

  func testReduceInto() {
    let ys = Array(repeating: 1, count: 1_000_000)
    var xs: [Int] = []
    xs.reserveCapacity(5_000_000)
    measure {
      xs = ys.reduce(into: xs) { xs, y in xs.append(y) }
      xs = ys.reduce(into: xs) { xs, y in xs.append(y) }
      xs = ys.reduce(into: xs) { xs, y in xs.append(y) }
      xs = ys.reduce(into: xs) { xs, y in xs.append(y) }
      xs = ys.reduce(into: xs) { xs, y in xs.append(y) }
    }
  }

The testReduceInto runs about 10x slower in an optimized build than testRawAccumulation.

This overload, however, performs the same as testRawAccumulation:

extension Sequence {
  @inlinable
  public func reduce<Result>(
    into result: inout Result,
    _ updateAccumulatingResult: (inout Result, Element) throws -> Void
    ) rethrows {

      for element in self {
        try updateAccumulatingResult(&result, element)
      }
  }
}

Is this a bug in the compiler? Should we revisit adding an overload?


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

Can a compiler or standard library engineer weigh in here? Any reason not to make a pitch?