Extension on Array where Element is generic type

Is there a way to define an extension on an Array where the Element is a type with a generic? I know it's possible to declare an extension if I make the generic a concrete type, but what if I want to remain generic here?

E.g.

extension Array where Element == Range<Date> {
    func foo() {
        // Do something
    }
}

Is there a way to specify Bound instead?

7 Likes

I believe you are asking for what the generics manifesto calls parameterized extensions.

I agree this would be useful, though it is not yet possible in Swift.

9 Likes

Ah, this is exactly it. In fact, I even tried this proposed syntax when seeing if I could get it work. At least I know there is a name for the proposed behaviour now and I can track it. Thanks Nevin.

The workaround for this kind of thing is to make a protocol that can produce the parameterized type that you want, like so:

protocol Rangey {
  associatedtype Bound: Comparable
  var range: Range<Bound> { get }
}

extension Range: Rangey {
  var range: Range<Bound> { return self }
}

extension Array where Element: Rangey, Element.Bound == Date {
  func contains(date: Date) -> Bool {
    return contains(where: { $0.range.contains(date) })
  }
}
7 Likes

This is the behaviour I am trying to achieve:

class Weak<Value: AnyObject> {
    weak var value: Value?

    init(_ value: Value) {
        self.value = value
    }
}

protocol Weakly {
    associatedtype Value: AnyObject
    var weak: Weak<Value> { get }
}

extension Weak: Weakly {
    var weak: Weak<Value> { return self }
}

extension Array where Element: Weakly {
    func contains(_ value: Weakly) -> Bool {
        return self.flatMap( { $0.weak.value } ).contains(value)
    }
}

I am trying to create a generic convenience contains() call on an array that contains Weak boxed types. Tried using your approach but couldn't quite get it how I wanted.

I think in the method you want the parameter to be Element.Value:

extension Array where Element: Weakly, Element.Value: Equatable {
    func contains(_ value: Element.Value) -> Bool {
        return self.contains(where: { $0.weak.value == value })
    }
}

This also uses contains in place to save the extra processing, and adds a constraint that the value has to be equatable.

3 Likes

Ah perfect. Got it working:

extension Array where Element: Weakly, Element.Value: Equatable {
    func contains(_ value: Element.Value) -> Bool {
        return self.flatMap( { $0.weak.value } ).contains(where: {$0 == value})
    }
}

I am flatMapping as I want the convenience of being able to pass in objects not boxed in Weak.

EDIT: Just realised that is not required at all!

Thanks!

5 Likes