I would like extend Array
to add conformance to a new protocol — but only for arrays whose elements themselves conform to a specific protocol.
More generally, I’d like to have types (whether protocols or concrete types) with type parameters implement a protocol only when the type parameters match certain constraints.
As of Swift 2.0, this appears to be impossible. Is there a way I’m missing?
Suppose we have the Friendly
protocol:
protocol Friendly { func sayHi() }
We can extend existing types to implement it:
extension String: Friendly { func sayHi() { print("Greetings from \(self)!") } } "Sally".sayHi()
We can also extend Array
to implement sayHi()
when its elements are all Friendly
:
extension Array where Element: Friendly { func sayHi() { for elem in self { elem.sayHi() } } } ["Sally", "Fred"].sayHi()
At this point, the type [Friendly]
should itself implement Friendly
, since it meets the protocol’s requirements. However, this code doesn’t compile:
extension Array: Friendly where Element: Friendly { func sayHi() { for elem in self { elem.sayHi() } } }
The error message is “extension of type 'Array' with constraints cannot have an inheritance clause,” which seems to shut the door definitively on that direct approach.
Is there an indirect workaround? Some clever trick I can use? Perhaps there’s a way that involves extending SequenceType
instead of Array
?
A working solution would make this code compile:
let friendly: Friendly = ["Foo", "Bar"]
Update: This has landed in Swift 4.1, and it is a thing of beauty!
The extension Array: Friendly where Element: Friendly
example now compiles as given in the original question.
In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions. Extensions can add new functionality to a type, but they can't override existing functionality.
Protocols let you describe what methods something should have, but don't provide the code inside. Extensions let you provide the code inside your methods, but only affect one data type – you can't add the method to lots of types at the same time.
Protocol extensions are different. You cannot “extend” a protocol because by definition a protocol doesn't have an implementation - so nothing to extend. (You could say that we “extend a protocol WITH some functionality”, but even an extended protocol is not something we can apply a function to.)
What is an associated type? An associated type can be seen as a replacement of a specific type within a protocol definition. In other words: it's a placeholder name of a type to use until the protocol is adopted and the exact type is specified.
EDIT: As noted in the updated question, this is now possible since Swift 4.1
This is not currently possible in Swift (as of Xcode 7.1). As the error indicates, you can't restrict protocol conformance ("inheritance clause") to a type-constrained extension. Maybe someday. I don't believe there's any deep reason for this to be impossible, but it's currently not implemented.
The closest you can get is to create a wrapper type such as:
struct FriendlyArray<Element: Friendly>: Friendly { let array: [Element] init(_ array: [Element]) { self.array = array } func sayHi() { for elem in array { elem.sayHi() } } } let friendly: Friendly = FriendlyArray(["Foo", "Bar"])
(You would likely want to extend FriendlyArray
to be a CollectionType
.)
For a tale of my own descent into the madness of trying to make this work, and my crawl back from the edge, see NSData, My Old Friend.
The good news is that what you are asking for Conditional Conformance
is coming in Swift 4.1:
https://swift.org/blog/conditional-conformance/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With