'Generic' method over a Sequence of Optionals

I have defined a simple 'generic' method over a sequence of Numeric
values:

extension Sequence where Element: Numeric {
    public func sum() -> Element {
        return reduce(0, +)
    }
}

I would like to write the corresponding method over a Sequence of optional
Numeric values, something like:

extension Sequence where Element == Optional<Numeric> {
    public func sum<T>() -> T where T: Numeric {
        // ???
    }
}

But, as hard as I try, I cannot make the type-checker happy. I am wondering,
is there a way to write such a 'generic' method?

I think you can find the answer in this similar thread.

1 Like

There's an easier answer than the one suggested in that thread. You can place constraints on the requirement itself instead of on the extension:

extension Sequence {
  public func sum<T: Numeric>() -> T where Element == Optional<T> { ... }
}
4 Likes

Thanks!

(Also, nice to see that support for this use-case is in the roadmap)

So, I followed your advice:

extension Sequence where Element: Numeric {
    public func sum() -> Element {
        return reduce(0, +)
    }
}

extension Sequence {
    public func sum<T>() -> T where T: Numeric, Element == T? {
        var sum: T = 0

        for value in self {
            if let value = value {
                sum += value
            }
        }

        return sum
    }
}

But when I use the method, I get ambiguity errors:

*****:70:24: error: ambiguous reference to member 'sum()'
        XCTAssertEqual(series.sum(), 948.872)
                       ^~~~~~
Statistics.Sequence:2:17: note: found this candidate
    public func sum() -> Self.Element
                ^
Statistics.Sequence:2:17: note: found this candidate
    public func sum<T>() -> T where T : Numeric, Self.Element == T?

Any ideas how to resolve these?

If there's a more specific name you can give your operation, that would avoid the ambiguity. Otherwise, you should be able to specify the type of the result you expect using as, series.sum() as Double, to force it to pick the method that returns Double rather than Double?.

But, please note, both methods return Double (not an optional). I would like the same method to work both for a sequence of Numeric and a sequence of Optional<Numeric>.

What's the type of your series variable? Those sum methods are mutually exclusive, and that error sounds like it might not be matching either one.

It's something like this:

extension DataSeries: BidirectionalCollection {
    public typealias Value = Any?
    public typealias Element = Value
    ...
}

let series: DataSeries

If needed, I can provide the full declaration.

Hmm, I think I get it now, it's because Any is not a Numeric.
Still, the error could be more descriptive.

1 Like

This should be filed. Mind doing it? https://bugs.swift.org

Well, here you are:

2 Likes