Convenience functions for sum and product across a Collection type

One issue I foresee is that we’ll want dynamic dispatch. If someone writes a custom generic algorithm that involves, say, summing a numeric collection:

extension Collection where Element: Numeric {
  func myFancyAlgorithm() -> Element {
    let sum = self.sum()
    // do something with sum
  }
}

Then that can be called with elements that are floating-point, or signed integers, or unsigned integers, and so forth.

If we just implement the various Sequence.sum() versions as constrained extension methods, then this will call whichever sum() applies to Numeric elements.

It will not look into the dynamic type of Element to see if it “wants” the overflow-counting behavior of signed fixed-width integers, or the compensated-sum of floating-point, or anything else.

• • •

The natural way to fix this is for the standard library to introduce a customization point in, say AdditiveArithmetic, which might be spelled:

static func _sum<S: Sequence>(_ s: S) -> Self where S.Element == Self

That would let us put custom implementations in places like extension FixedWidthInteger where Element: SignedInteger, and conforming types would pick them up naturally.

Then Sequence.sum() could have a one-line implementation that just calls Element._sum(self), and the algorithm would be dynamically dispatched through the element type.

• • •

That’s all well and good, and it would work great for the standard integer and floating-point types.

Of course, for the standard library to add a customization point to a protocol, it must also provide a default implementation along with it. The only reasonable choices for that are to trap, which is not desirable, or to use the general-purpose implementation reduce(.zero, +).

The reduce implementation is a perfectly fine default, but it makes things difficult for generic types that prefer a better algorithm.

This isn’t too bad for something like Complex whose generic parameter is always floating-point, but even there the Numerics library would want to provide a custom implementation of Complex._sum() that essentially duplicates the standard version for floating-point, perhaps with a special-case branch for Float and Float16.

• • •

However, one can imagine a Matrix<Element> type, whose Element has no intrinsic constraints. Matrix would conditionally conform to AdditiveArithmetic when its Element does, so it would have to provide a single implementation of _sum() that works for integers, floats, complex values, and other matrices, as well as 3rd-party types.

It is perfectly sensible to sum a sequence of matrices (provided their dimensions match), but there’s no good way for the implementation of Matrix._sum() to make use of Element._sum() when doing so.

If we constrained to Collection instead of Sequence then Matrix._sum() could do a multi-pass traversal, summing each position separately with Element._sum() via a lazy map of the collection.

That would make it easier for Matrix to provide a correct conformance, but it would still leave the pitfall that the author of Matrix might not notice they need to implement _sum(), since the default implementation would be present and would even work correctly for some Element types.

It also would seem like a rather arbitrary restriction, since summing makes perfectly good sense for single-pass sequences.