Using protocol with generics in Swift

I am trying to use protocols with generics but having trouble with syntax. In my first example, I have a first protocol declared as


protocol Consumer {

typealias T

func accept(arg: T)

}


I then declare another protocol for executing Consumer as

protocol Scheduler {

func schedule<T>(consumer : Consumer<T>, arg: T)

}


However, I get error about 'Consumer' can only be used as generic constraint because it has Self or associated type requirement



Here is another example, I have a protocol as


protocol Observable {

typealias T

func map<U>(function : (T) -> U) -> Observable<U>

}

I am trying to define a function that converts T to U and then return Observable with U type but getting similar error as above.

Can someone suggest correct syntax because coming from Java background above code seemed natural to me.

Thanks.

How about this:


protocol Consumer {
  typealias T
  func accept(arg: T)
}

protocol Scheduler {
  func schedule<C : Consumer>(consumer : C, arg: C.T)
}


And:

protocol Observable {
  typealias T
  func map<O : Observable>(function : (T) -> O.T) -> O
}

Thanks that worked for me, though I have another use case. I need to define zip function that takes Observable with another type and returns new Observable that uses tuple to zip to observables. Any more ideas?

func zip<O : Observable>(other : O) -> Observable<(T,O.T)>


Thanks again.

You can not declare Protocols which have typealiases as return type of a function (or as type of a variable)

You could declare the function as follows:

func zip<O : Observable, R : Observable where R.T == (T, O.T)>(other : O) -> R

But it would not be exactly what you want to express and I guess there would be no sensible way of implementing it because you would need to satisfy whatever return type the caller demands.

You could take a look at this thread: https://forums.developer.apple.com/thread/7350

The accepted answer describes a way of wrapping an protocol type into an Any* class which can then be used as return value. But I guess that will not even work for you because it makes use of closures which can not be generic (afaik)

In some use cases, your method declaration can have a useful implementation.

protocol Observable {
    typealias T
    func map<O : Observable>(function : (T) -> O.T) -> O
    func zip<O : Observable, R: Observable where R.T == (T, O.T)>(other : O) -> R

    //Placed here to write default implementaions
    init(value: T)
    var value: T {get}
}
extension Observable {
    func map<O : Observable>(function: (T) -> O.T) -> O {
        return O(value: function(self.value))
    }
    func zip<O : Observable, R: Observable where R.T == (T, O.T)>(other : O) -> R {
        return R(value: (self.value, other.value))
    }
}
class ObservableImpl<T>: Observable {
    var value: T
    required init(value: T) {
        self.value = value
    }
}
struct AnotherObservableImpl<T>: Observable {
    var value: T
    init(value: T) {
        self.value = value
    }
}
let observableInt = ObservableImpl<Int>(value: 1)
//let observableDouble = observableInt.map(Double.init) //Causes error
let observableDouble: ObservableImpl = observableInt.map(Double.init)
let anotherObservableDouble: AnotherObservableImpl = observableInt.map(Double.init)
let zippedObservable: ObservableImpl = observableInt.zip(observableDouble)

Well you are right. But in this case the zip method is limited to just be a wrapper around the initializer.


As the protocol name used here is "Observable" I would assume the zipping should happen dynamicly and update the zipped value whenever one of the the zipped values change. I guess to achieve this you would want to create something like a dedicated ZippedObserverable which keeps references to the two source obserables, right?


Ultimately there is not point in having a zip method if the only possible implementation is a trivial delegation to a initializer.

I think your assumption is very probable and reasonable. So I wrote "in some use cases", maybe not many, maybe not "none".

Using protocol with generics in Swift
 
 
Q