How to write an extension for eg Collection where Element == Optional<T>?

Note that I’m only using Collection and Optional here to provide a simple (albeit contrived) example of a more general question.

(The more general question perhaps being something like: “Why can’t an associated type (eg Element) be constrained to being any particular concretization of some generic type (eg Optional<T>)? And how can this limitation be worked around in this specific example?”)

Anyway, It’s probably better to just focus on the question at the end of this little program:

extension Collection where Element == Optional<Float> {
    var someElementCount : Int {
        return self.reduce(0) { (partialResult, element) in
            switch element {
            case .none: return partialResult
            case .some: return partialResult + 1
            }
        }
    }
    var noneElementCount : Int { return count - someElementCount }
}

let a: [Float?] = [1.2, 3.4, nil]
print(a.count) // 3
print(a.someElementCount) // 2
print(a.noneElementCount) // 1

// Now, I would of course like to have this functionality for any `Optional<T>`
// (and not just for `Optional<Float>`). So eg the following should also work:
let b: [String?] = ["abc", nil, "ghi"]
print(b.count) // 3
// print(b.someElementCount) // Should print 2
// print(b.noneElementCount) // Should print 1

// How do I accomplish this? (Without type erasing to [Any?])

It’s not pretty, and forgive the naming, etc, but

protocol OptionalConvertible {
    associatedtype Element
    var asOptional: Optional<Element> { get }
}

extension Optional: OptionalConvertible {
    var asOptional: Optional<Wrapped> { return self }
}

extension Collection where Element: OptionalConvertible {
    var someElementCount : Int {
        return self.reduce(0) { (partialResult, element) in
            switch element.asOptional {
            case .none: return partialResult
            case .some: return partialResult + 1
            }
        }
    }
    var noneElementCount : Int { return Int(count) - someElementCount }
}

let a: [Float?] = [1.2, 3.4, nil]
print(a.count) // 3
print(a.someElementCount) // 2
print(a.noneElementCount) // 1

let b: [String?] = ["abc", nil, "ghi"]
print(b.count) // 3
print(b.someElementCount) // Should print 2
print(b.noneElementCount) // Should print 1
1 Like

The feature you’re asking for is simply not implemented yet.

1 Like

Thanks! I could only come up with workarounds similar to the one @jawbroken gave, so I figured I was missing something obvious or that there was something missing in the language.