77

I'd like to use the Accelerate framework to extend [Float] and [Double] but each of these requires a different implementation.

I tried the obvious:

extension Array<Float> {
}

and get this error:

"Constrained extension must be declared on the unspecialised generic type 'Array' with constraints specified by a 'where' clause"

Is it posible to extend generic types in Swift 2 in this way?

I've got the code working as expected now. Here's an example showing a summation using the Accelerate framework.

extension _ArrayType where Generator.Element == Float {

    func quickSum() -> Float {
        var result: Float = 0
        if var x = self as? [Float] {
            vDSP_sve(&x, 1, &result, vDSP_Length(x.count))
        }
        return result
    }
}

extension _ArrayType where Generator.Element == Double {

    func quickSum() -> Double {
        var result: Double = 0
        if var x = self as? [Double] {
            vDSP_sveD(&x, 1, &result, vDSP_Length(x.count))
        }
        return result
    }
}
GScrivs
  • 865
  • 1
  • 7
  • 19

8 Answers8

131

If you want to extend only array with specific type. You should extend _ArrayType protocol.

extension _ArrayType where Generator.Element == Int {

   func doSomething() {
       ... 
   }
}

If you extend Array you can only make sure your element is conformed some protocol else. i.e:

extension Array where Element: Equatable {

   func doSomething() {
       ... 
   }
}

Updated: With Swift 3.1 https://github.com/apple/swift/blob/master/CHANGELOG.md

extension Array where Element == Int {

   func doSomething() {
       ... 
   }
}
Huy Le
  • 2,503
  • 1
  • 15
  • 15
  • This really helped me for a slightly different problem. I was overriding Array and using contains(element) which did not work until I constrained the extension to Element : Equatable. To see what the different constraints are (and what methods they affect) see also SequenceType: http://swiftdoc.org/v2.1/protocol/SequenceType/ – David James Mar 04 '16 at 20:15
  • I think you should use `SequenceType` instead of `_ArrayType` – Daniel Jul 13 '16 at 12:46
  • 8
    The answer works perfectly with Swift 2.2, but _ArrayType has disappeared in Swift 3. How should we do it now? – Maiaux Sep 15 '16 at 07:55
  • @Maiaux See my answer below. – Ben Lu Sep 28 '16 at 23:06
  • 15
    In swift 3.1, `extension Array where Element == Int` gives error **error: same-type requirement makes generic parameter 'Element' non-generic extension Array where Element == Int**, give me suggestion – Rohit Parsana Nov 22 '16 at 10:41
  • @BenLu Your answer works but now I don't have access to subscripts for the collection. For instance, if I write self[0] I get a "Cannot subscript a value of type Self with an index of type Int" error – Maiaux Jan 15 '17 at 16:45
29

Swift 3 to the rescue!!

extension Collection where Iterator.Element == Int {
    // `Collection` can be `Sequence`, etc
}
Ben Lu
  • 2,982
  • 4
  • 31
  • 54
  • 1
    This works, but now I don't have access to subscripts for the collection. For instance, if I write `self[0]` I get a "Cannot subscript a value of type Self with an index of type Int" error – Maiaux Jan 15 '17 at 16:27
  • @Maiaux because `subscript` is not a function and does not support generics, so I guess this doesn't work too well. – Ben Lu Jan 20 '17 at 02:48
  • Any other swift 3 solution that supports subscripts? – Maiaux Jan 20 '17 at 09:18
16

How about

extension CollectionType where Generator.Element == Double {

}

Or If you want a little bit more:

protocol ArithmeticType {
    func +(lhs: Self, rhs: Self) -> Self
    func -(lhs: Self, rhs: Self) -> Self
    func *(lhs: Self, rhs: Self) -> Self
    func /(lhs: Self, rhs: Self) -> Self
}

extension Double : ArithmeticType {}
extension Float : ArithmeticType {}

extension SequenceType where Generator.Element : protocol<FloatLiteralConvertible, ArithmeticType> {
    var sum : Generator.Element {
        return reduce(0.0, combine: +)
    }

    var product : Generator.Element {
        return reduce(1.0, combine: *)
    }
}


stride(from: 1.0, through: 10.0, by: 1.0).sum   // 55
[1.5, 2.0, 3.5, 4.0, 5.5].product               // 231

Works with Double and Float or any other type that you conform to the protocols ArithmeticType and FloatLiteralConvertible. If you need to access specific indices of your array, change SequenceType to CollectionType as you cannot do this with a sequence.

Kametrixom
  • 14,673
  • 7
  • 45
  • 62
5

Swift 3 on Xcode 8.2

Just need to extend Sequence protocol and provide a where statement.

let someString = "1, 2, 3, 4, 5, 6, 7, 8"

extension String {        
  func toArrayOfElements() -> [String] {
    return self.components(separatedBy: ", ")
  }        
}

extension Sequence where Iterator.Element == String {        
  func toInt() -> [Int] {            
    return self.map {
      Int($0)!
    }
  }        
}

let arrayOfStrings = someString.toArrayOfElements()    
print(arrayOfStrings)

let arrayOfInts = arrayOfStrings.toInt()    
print(arrayOfInts)
Jeroen Heier
  • 3,520
  • 15
  • 31
  • 32
Melvin John
  • 71
  • 1
  • 3
4

So I didn't read the question properly. FloatingPointType is an existing protocol that is implemented by Double, Float and CGFloat, so

Yes. I did it only yesterday to add a function to SequenceType where the elements had to be Equatable. This is a modification to restrict the elements to Float

You need to use a where clause. This is my function below.

public extension SequenceType where Self.Generator.Element: FloatingPointType
{
    public func splitAt(separator: Generator.Element) -> [[Generator.Element]]
    {
        var ret: [[Generator.Element]] = []
        var thisPart: [Generator.Element] = []

        for element in self
        {
            if element == separator
            {
                ret.append(thisPart)
                thisPart = []
            }
            else
            {
                thisPart.append(element)
            }
        }
        ret.append(thisPart)
        return ret
    }
}

[Float(1), Float(2), Float(3), Float(4)].splitAt(Float(2))
// returns [[1],[3, 4]]
[Double(1), Double(2), Double(3), Double(4)].splitAt(Double(3))
// returns [[1, 2],[4]]

NB I couldn't make this work for an array but SequenceType is more general anyway.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
4

If you only want to extend a specific Array you have to use a protocol for each type:

protocol DoubleValue {
    var value: Double { get }
}
extension Double: DoubleValue {
    var value: Double { return self }
}
extension Array where Element: DoubleValue {
    // use the value property
}

// the same for Float
protocol FloatValue {
    var value: Float { get }
}

extension Float: FloatValue {
    var value: Float { return self }
}
extension Array where Element: FloatValue {
    // use the value property
}
Qbyte
  • 12,753
  • 4
  • 41
  • 57
1

This worked for me. I'm using Swift 5.

extension Array where Iterator.Element == Float {
}
pableiros
  • 14,932
  • 12
  • 99
  • 105
Sayalee Pote
  • 511
  • 5
  • 22
0

This is what it worked for me using Swift 5:

extension Array where ArrayLiteralElement == Float {

}
pableiros
  • 14,932
  • 12
  • 99
  • 105