Extension of constructed generic type in Swift
This can be achieved using protocol extensions (See The Swift Programming Language: Protocols for more information). In Swift 3:
To sum just Int
s you could do:
extension Sequence where Iterator.Element == Int {
var sum: Int {
return reduce(0, +)
}
}
Usage:
let nums = [1, 2, 3, 4]
print(nums.sum) // Prints: "10"
Or, for something more generic you could what @Wes Campaigne suggested and create an Addable
protocol:
protocol Addable {
init()
func + (lhs: Self, rhs: Self) -> Self
}
extension Int : Addable {}
extension Double: Addable {}
extension String: Addable {}
...
Next, extend Sequence
to add sequences of Addable
elements:
extension Sequence where Iterator.Element: Addable {
var sum: Iterator.Element {
return reduce(Iterator.Element(), +)
}
}
Usage:
let doubles = [1.0, 2.0, 3.0, 4.0]
print(doubles.sum) // Prints: "10.0"
let strings = ["a", "b", "c"]
print(strings.sum) // Prints: "abc"
Managed to get something working in an extensible, generic fashion without abusing the type system too badly, however it has some limitations.
protocol Addable {
func +(lhs: Self, rhs: Self) -> Self
class var identity: Self { get }
}
extension Int : Addable {
static var identity: Int { get { return 0 } }
}
extension String : Addable {
static var identity: String { get { return "" } }
}
extension Array {
func sum<U : Addable>() -> U? {
let s: U? = U.identity
return self.sum(s)
}
func sum<U : Addable>(start: U?) -> U? {
return reduce(start) { lhs, rhs in
switch (lhs, rhs) {
case (.Some(let left), let right as U):
return left + right
default:
return nil
}
}
}
}
Specifically: with this solution, type inferencing won't work on the no-parameter sum()
method, so you have to either annotate the expected return type or give it a starting value (from which it can infer the type).
Note also that this returns a value of Optional type: if for any reason a sum of the expected type cannot be computed from the array, it returns nil.
To illustrate:
let int_array = Array(1...10)
let x: Int? = int_array.sum() // result: {Some 55}
let x2 = int_array.sum(0) // result: {Some 55}
let x3 = int_array.sum() // Compiler error because it can't infer type
let string_array = ["a", "b", "c"]
let y: String? = string_array.sum() // result: {Some "abc"}
let y2 = string_array.sum("") // result: {Some "abc"}
let y3: Int? = string_array.sum() // result: nil (can't cast String to Int)
let y4 = string_array.sum(0) // result: nil (can't cast String to Int)
let double_array = [1.3, 4.2, 2.1]
let z = double_array.sum(0.0) // Compiler error because we haven't extended Double to be Addable