Swift array of protocols with associatedtype [duplicate] - swift

This question already has an answer here:
How do I add different types conforming to a protocol with an associated type to a collection?
(1 answer)
Closed 6 years ago.
I have the first protocol
protocol Prot1
{
}
And the second:
protocol Prot2
{
associatedtype P: Prot1
func doSomething(param: P)
}
How can i make an array with the type Prot2?
I tried:
var myArray = [Prot2]()
But it gives me this error: Protocol Prot2 can only be used as a generic constraint because it has Self or associated type requirements
Is there any other way to make a template protocol?
EDIT:
Sorry if i am late, but i was testing the solutions.
As far as i understand the type-erase
AnyProt2<Prot1Type: Prot1>: Prot2
let me have a an array with
[AnyProt2<Prot1Class>]
but i was asking for an array that could contain all kinds of AnyProt2 something like:
[AnyProt2] or [AnyProt2<Prot1>]
I tried the second but it gives me:
Using 'Prot1' as a concrete type conforming to protocol 'Prot1' is not supported.
In my context Prot1 is Interval and Prot2 is a Event, so i wanted to have and array with different kinds of Events that could have different kinds of Intervals.
I also want to ask if it's possible to make Prot1 to extend Equatable and confirm the protocol in AnyProt2.

Depending on your use case, you might get away with using generics, like so:
protocol Prot1 {
}
protocol Prot2 {
associatedtype P: Prot1
func doSomething(param: P)
}
class MyClass<T: Prot2> {
var arr: [T] = []
}
But this solution pretty much defers the specifying of the concrete type implementing Prot2 and making consumer of MyClass to deal with that.
Sounds like your case would require to use something called "type erasure". Unfortunately, I won't be able to explain it here, but here are some really great articles and even talks that helped me to understand the concept:
https://www.natashatherobot.com/swift-type-erasure/
Video: https://realm.io/news/tryswift-gwendolyn-weston-type-erasure/
http://krakendev.io/blog/generic-protocols-and-their-shortcomings
http://robnapier.net/erasure
https://realm.io/news/type-erased-wrappers-in-swift/

Related

Can I require that a protocol's associated type is a protocol itself in Swift?

Question
I have a protocol defined as follows:
protocol MyProtocol {
associatedtype MySubprotocol
}
Can I require that the associated type MySubprotocol is a protocol as well?
So semantically it would be sth. like this:
protocol MyProtocol {
associatedtype MySubprotocol: AnyProtocol
}
Background
I know that I can do nothing with that protocol inside a given concrete type that implements MyProtocol, but I can use it "from outside" for protocol composition. My ultimate goal would be to define a "Sum protocol" from all the associated types of all MyProtocol implementations, e.g. when I have the following structs that implement MyProtocol,
struct MyImpl_1: MyProtocol {
typealias MySubProtocol = MySubProtocol_1
}
struct MyImpl_2: MyProtocol {
typealias MySubProtocol = MySubProtocol_2
}
...
I want to create a composite protocol like this:
protocol AllRequirements:
MyImpl_1.MySubprotocol,
MyImpl_2.MySubprotocol,
... {}
This requires MyImpl_1.MySubprotocol etc. to be a protocol, otherwise I can't compose it. (I know, I could use the concrete type MySubProtocol_1 instead, but I want compile-time safety when a developer creates a new implementation of MyProtocol (i.e. another MyImpl_n).
Example Use Case
I have a couple of "Worker" objects, each of which performs a specific task. As I want to enforce a common API, I create a common protocol for all of these types:
protocol: Worker {
associatedtype Toolset
func getTheJobDone()
}
Here's an example for a concrete type:
struct Cleaner: Worker {
typealias Toolset = AnyBrush
let brush: AnyBrush
init(brush: AnyBrush) {
self.brush = brush
}
func getTheJobDone() {
brush.applyToFloor()
}
}
In practice, I will have multiple types of this sort to perform different tasks. I want each of these Workers to define their own requirements, i.e. the interface (protocol) they require to get their job done. So in this case, I would create a protocol AnyBrush along with the Cleaner:
protocol AnyBrush {
func applyToFloor()
}
which I then require as the worker's Toolset (see above).
This approach works for me, but when I create a new Worker type, the current protocol definition will allow me to specify any type for the Toolset, so someone could (for some unknown reason) do this:
typealias Toolset = Int
which doesn't make sense at all.
So that's why I'm asking this question. For these two goals:
Force a common interface (API) for all workers.
Use the compiler to enforce that the associated type Toolset is always a protocol.
One might ask me to trust my co-workers and my future self that we're going to use a protocol because if we don't understand that, we got a much more serious problem, but I would still like to see if it's possible to require a protocol.

Set of protocols

Suppose I have this:
protocol MyStuff: Hashable {
var stuff: String { get }
}
extension MyStuff {
func hash(into hasher: inout Hasher) {
hasher.combine(stuff)
}
static func == (lhs: Self, rhs: Self) -> Bool {
return lhs.stuff == rhs.stuff
}
}
struct Stuff: MyStuff {
let stuff: String
}
Where I have a protocol which conforms to Hashable, and I extend that protocol to implement the requirements of Hashable. This works great.
I'm now trying to use the api like this:
let set: Set<MyStuff> = [Stuff(stuff: "Stuff")]
However I get this error:
Protocol 'MyStuff' as a type cannot conform to 'Hashable'
This question is actually a follow up to this answer written by George, and in that answer it says that
I believe SE-0309 (Unlock existentials for all protocols) could fix this.
I was wondering if that (the above link) would actually apply into this situation (listed in the code block above), as I'm having trouble fully understanding the proposal.
There are a couple of issues here, not necessarily due to the code you wrote, but due to how Swift is currently designed:
Protocols don't conform to other protocols, which means that MyStuff is not a sub-type of Hashable, which means you cannot use it as argument for the generic Set
Protocols with associated types, or self requirements, like Equatable, from which Hashable derives, can't be used as generic arguments, can only be used as generic constraints
The compiler runs into issue #1 from above, and that one gives the (clear maybe) message that protocols as types cannot be used as generic arguments instead of other protocols.
SE-0309 might solve issue #2, however you're stuck on #1, so you'll have to change your design.
Solutions? As others have suggested in the comments:
use a type eraser (e.g. https://stackoverflow.com/a/64476569/1974224)
use classes, and replace the protocol by a base class

Constraining Generic Type extensions with inheritance [duplicate]

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/

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.

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/