Constraining Generic Type extensions with inheritance [duplicate] - swift

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?
Example
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.

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/

Related

Define a protocol that holds Collection as a property

I want to define a protocol that defines a variable "sequence" that is of type "Collection". I want that, because i have several conforming types, that will all hold an array of different types. Example:
protocol BioSequence {
associatedtype T
var sequence: Collection { get }
// Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}
struct DNASequence: BioSequence {
// Will hold "sequence" of type [Nucleotide]
}
struct AminoSequence: BioSequence {
// Will hold "sequence" of type [AminoAcid]
}
Why do i want this? Because i need to implement the conformance to "Collection" only once in BioSequence and all conforming type inherit it automatically. Plus i can freely add extra functionality on the conforming types.
Now, when i try this like the code above, the compiler says: "Protocol Collection can only be used as a generic constraint". Yes, i googled what this error means, but how can i actually fix it to make my code work, like i want. Or is it not even possible to do what i want?
Thank you.
You can easily achieve this by using an associatedtype in your protocol, which can be constrained to Collection, allowing conforming types to satisfy the requirement with a concrete type when they adopt the protocol.
For example:
protocol CollectionWrapper : Collection {
associatedtype Base : Collection
var base: Base { get }
}
extension CollectionWrapper {
var startIndex: Base.Index {
return base.startIndex
}
var endIndex: Base.Index {
return base.endIndex
}
func index(after i: Base.Index) -> Base.Index {
return base.index(after: i)
}
subscript(index: Base.Index) -> Base.Iterator.Element {
return base[index]
}
// Note that Collection has default implementations for the rest of the
// requirements. You may want to explicitly implement them and forward them to
// the base collection though, as it's possible the base collection implements
// them in a more efficient manner (e.g being non random access and having
// a stored count).
}
// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
var base: [String]
}
let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"
Regarding your comment:
If I want to use this like that: let a: CollectionWrapper = S() [...] this leaves me with "Protocol can only be used as a generic constraint" again.
The problem is that you cannot currently talk in terms of a protocol with associated type requirements, as the types which are used to satisfy those requirements are unknown to the compiler (this isn't a technical limitation though). You can solve this by using Swift's AnyCollection type eraser to wrap an arbitrary Collection with a given element type.
For example:
let a = AnyCollection(S(base: ["foo", "bar", "baz"]))
If you need to work with additional protocol requirements from CollectionWrapper, you'll have to implement your own type eraser to do so. See Rob's answer here for an idea of how to go about this.

Swift: Is it possible to add a protocol extension to a protocol?

Lets say I have two protocols:
protocol TheirPcol {}
protocol MyPcol {
func extraFunc()
}
What I want to do is to create a protocol extension for 'TheirPcol' which lets extraFunc() work on anything which conforms to 'TheirPcol'. So something like this:
extension TheirPcol : MyPcol { // Error 'Extension of protocol 'TheirPcol' cannot have an inheritance clause.
func extraFunc() { /* do magic */}
}
struct TheirStruct:TheirPcol {}
let inst = TheirStruct()
inst.extraFunc()
The kicker in this is that 'TheirPcol', 'TheirStruct' are all handled by an external API which I do not control. So I'm passed the instance 'inst'.
Can this be done? Or am I going to have to do something like this:
struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()
It seems there are two use-cases of why you may want to do what you are doing. In the first use-case, Swift will allow you to do what you want, but not very cleanly in the second use-case. I'm guessing you fall into the second category, but I'll go through both.
Extending the functionality of TheirPcol
One reason why you might want to do this is simply to give extra functionality to TheirPcol. Just like the compiler error says, you cannot extend Swift protocols to conform to other protocols. However, you can simply extend TheirPcol.
extension TheirPcol {
func extraFunc() { /* do magic */ }
}
Here, you are giving all objects that conform to TheirPcol the method extraFunc() and giving it a default implementation. This accomplishes the task of extending functionality for the objects conforming to TheirPcol, and if you want it to apply to your own objects as well then you could conform your objects to TheirPcol. In many situations, however, you want to keep MyPcol as your primary protocol and just treat TheirPcol as conforming to MyPcol. Unfortunately, Swift does not currently support protocol extensions declaring conformance to other protocols.
Using TheirPcol objects as if they were MyPcol
In the use case (most likely your use case) where you really do need the separate existence of MyPcol, then as far as I am aware there is no clean way to do what you want yet. Here's a few working but non-ideal solutions:
Wrapper around TheirPcol
One potentially messy approach would be to have a struct or class like the following:
struct TheirPcolWrapper<T: TheirPcol>: MyPcol {
var object: T
func extraFunc() { /* Do magic using object */ }
}
You could theoretically use this struct as an alternative to casting, as in your example, when you need to make an existing object instance conform to MyPcol. Or, if you have functions that accept MyPcol as a generic parameter, you could create equivalent functions that take in TheirPcol, then convert it to TheirPcolWrapper and send it off to the other function taking in MyPcol.
Another thing to note is if you are being passed an object of TheirPcol, then you won't be able to create a TheirPcolWrapper instance without first casting it down to an explicit type. This is due to some generics limitations of Swift. So, an object like this could be an alternative:
struct TheirPcolWrapper: MyPcol {
var object: MyPcol
func extraFunc() { /* Do magic using object */ }
}
This would mean you could create a TheirPcolWrapper instance without knowing the explicit type of the TheirPcol you are given.
For a large project, though, both of these could get messy really fast.
Extending individual objects using a child protocol
Yet another non-ideal solution is to extend each object that you know conforms to TheirPcol and that you know you wish to support. For example, suppose you know that ObjectA and ObjectB conform to TheirPcol. You could create a child protocol of MyPcol and then explicitly declare conformance for both objects, as below:
protocol BridgedToMyPcol: TheirPcol, MyPcol {}
extension BridgedToMyPcol {
func extraFunc() {
// Do magic here, given that the object is guaranteed to conform to TheirPcol
}
}
extension ObjectA: BridgedToMyPcol {}
extension ObjectB: BridgedToMyPcol {}
Unfortunately, this approach breaks down if there are a large number of objects that you wish to support, or if you cannot know ahead of time what the objects will be. It also becomes a problem when you don't know the explicit type of a TheirPcol you are given, although you can use type(of:) to get a metatype.
A note about Swift 4
You should check out Conditional conformances, a proposal accepted for inclusion in Swift 4. Specifically, this proposal outlines the ability to have the following extension:
extension Array: Equatable where Element: Equatable {
static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}
While this is not quite what you are asking, at the bottom you'll find "Alternatives considered", which has a sub-section called "Extending protocols to conform to protocols", which is much more what you're trying to do. It provides the following example:
extension Collection: Equatable where Iterator.Element: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
// ...
}
}
Then states the following:
This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.
While I realize you're not asking for the ability to have conditional conformances, this is the closest thing I could find regarding discussion of protocols being extended to conform to other protocols.

Swift Extensions for Collections

I'm working on a framework to make it easier to work with Key Value Observing and I've defined a protocol for converting native Swift types to NSObject as follows:
public protocol NSObjectConvertible {
func toNSObject () -> NSObject
}
Extending the builtin types was easy, simply defining the function to convert the given type to the appropriate NSObject:
extension Int8: NSObjectConvertible {
public func toNSObject () -> NSObject {
return NSNumber(char: self)
}
}
When I got to the Array type, I hit a number of snags, which I tried to work out. I didn't want to extend any array type, but only arrays whose element type was itself NSObjectConvertible. And naturally, needed Array to itself conform to the protocol.
After hunting around on SO, it looks like extending the Array type itself is a little harder because it's generic, but extending SequenceType can be done. Except that I can't both constrain the element type and declare its conformance to the protocol in the same declaration.
The following:
extension SequenceType where Generator.Element == NSObjectConvertible : NSObjectConvertible = {
public func toNSObject () -> NSObject {
return self.map() { return $0.toNSObject() }
}
}
Produces a compiler error:
Expected '{' in extension
And the carat points to the ":" where I'm trying to declare the protocol conformance. Removing the protocol conformance compiles without errors, but obviously doesn't help the case.
I'm not sure if this is a bug, or if Swift simply can't (or doesn't want to) support what I'm trying to do. Even if I simply define the extension, then try to take care of the conformance in the body, it produces the risk of passing sequences that don't really conform to what they should.
At best it's a hacky solution to just fail in cases where a sequence with non-conforming members are passed. I'd much rather let the compiler prevent it from happening.
(This is in Swift 2.1, Xcode 7.1.1)
You can't add the protocol conformance, unfortunately.

Is it possible to add type constraints to a Swift protocol conformance extension?

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?
Example
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.
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/

Conditionally lifting protocols to generic types in Swift

How do I say in Swift's type system "an Array<T> conforms to protocol P if the element type T conforms to protocol Q"?
I'm actually interested in a more specific version of this problem, where P and Q are the same protocol: you're saying "if the elements of the array are P-conforming, then the array is P-conforming". Here's what I have so far. (I'm trying for a simple QuickCheck library, starting from http://chris.eidhof.nl/posts/quickcheck-in-swift.html: Arbitrary marks types that can be randomly generated.)
protocol Arbitrary {
class func arbitrary() -> Self
}
extension Array {
static func arbitrary<T where T : Arbitrary>() -> [T] {
// code to create a random-length list of T objects
// using T.arbitrary() for each one
}
}
extension Array<T where T : Arbitrary> : Arbitrary {}
This fails with the error
extension of generic type 'Array' cannot add requirements
extension Array<T where T : Arbitrary> : Arbitrary {}
You can't do this in Swift, since you can't further constrain a generic type. For example, you can't add methods to Array<T> that only work when T is Comparable - that's why there are so many global functions for dealing with generic types (map, filter, sort, etc.).
From a recent Chris Lattner posts in the dev forums, it sounds like the Swift developers are headed in this direction, but it's nowhere near this yet. See if you can implement what you're trying to do as global functions that constrain T to Arbitrary:
func arbitrary<T: Arbitrary>() -> [T] {
// ..
}