Search code examples
swifttype-constraintsgenericsswift-extensions

Generalized type constraints with Swift


As an exercise, I'm trying to extend Array in Swift to add a sum() member function. This should be type safe in a way that I want a call to sum() to compile only if the array holds elements that can be added up.

I tried a few variants of something like this:

extension Array {

    func sum<U : _IntegerArithmeticType where U == T>() -> Int {
        var acc = 0
        for elem in self {
            acc += elem as Int
        }
        return acc
    }

}

The idea was to say, “OK, this is a generic function, the generic type must be something like an Int, and must also be the same as T, the type of the elements of the array”. But the compiler complains: “Same-type requirement make generic parameters U and T equivalent”. That's right, and they should be, with the additional contraint T : _IntegerArithmeticType.

Why isn't the compiler letting me do this? How can I do it?

(I know that I should later fix how things are added up and what the return type exactly is, but I'm stuck at the type constraint for now.)


Solution

  • As per Martin R's comment, this is not currently possible. The thing I'm tempted to use in this particular situation would be an explicit passing of a T -> Int conversion function:

    extension Array {
    
        func sum(toInt: T -> Int?) -> Int {
            var acc = 0
            for elem in self {
                if let i = toInt(elem) {
                    acc += i
                }
            }
            return acc
        }
    
    }
    

    Then I can write stuff like this:

    func itself<T>(t: T) -> T {
        return t
    }
    
    let ss = ["1", "2", "3", "4", "five"].sum { $0.toInt() }
    let si = [1, 2, 3, 4].sum(itself)
    

    An explicit function has to be passed, though. The (itself) part can of course be replaced by { $0 }. (Others have called the itself function identity.)

    Note that an A -> B function can be passed when A -> B? is needed.