I am trying to migrate the Visitor pattern from my (old) java code to swift. So far I have a generic FIFO (working fine).
protocol FiFo {
associatedtype U
func enqueue(_ : U)
func dequeue() -> U
}
I also want to tell the instance of the FIFO only to accept an instance of a visitor that uses the same type for the generic as the instance of the FIFO.
protocol Visitor {
associatedtype T
func processValue(_ value : T)
}
protocol FiFo {
associatedtype U
func enqueue(_ : U)
func dequeue() -> U
func visit(_ visitor : Visitor<U>)
}
Here I am facing:
Cannot specialize non-generic type 'Visitor'
Any hints? Thanks!
You can make the visit method generic: Accept any visitor whose associated type T is the fifo's type U:
protocol FiFo {
associatedtype U
func enqueue(_ : U)
func dequeue() -> U
func visit<V: Visitor>(_ visitor : V) where V.T == U
}
You can add a constraint to an associated type:
protocol Visitor {
associatedtype T
func processValue(_ value : T)
}
protocol FiFo {
associatedtype U
associatedtype V: Visitor where V.T == U
func enqueue(_ : U)
func dequeue() -> U
func visit(_ visitor: V)
}
Related
Is there any way to use the generic protocol as a data type in a function?
public protocol ICRUDOperation {
associatedtype T
func insert(data:T)
func update(data:T)
func get(data:T) -> [T]
func GetList(data: BaseModel) -> Array<T>
func GetPage(data: BaseModel) -> Array<T>
func Delete(data: T)
}
In the below function, I get the Error
func Delegate1<W: ICRUDOperation>(sqlite: W, service: W, data: T) {
sqlite.insert(data: data)
}
error: Cannot convert value of type 'T' (generic parameter of generic
class 'Decision') to expected argument type 'W.T' (associated type of
protocol 'ICRUDOperation')
As error stated, T is a generic parameter whose class is not defined in function declaration because T is associated with ICRUDOperation so you should use your delegate as:
func Delegate1<W: ICRUDOperation>(sqlite: W, service: W, data: W.T) {
sqlite.insert(data: data)
}
Assume, you have a PAT:
protocol PAT {
associatedtype T
func provide() -> T
}
And another protocol, that uses that protocol as a type constraint:
protocol RegularProtocol {
func consume<P: PAT>(_ pat: P) -> P.T
}
Is there a way to implement that second protocol for a certain associated type of PAT? For example, it would be great to have it if it were possible something like:
struct Consumer: RegularProtocol /*!*/ where RegularProtocol.T == () /*!*/ {
func consume<P: PAT>(_ pat: P) {
// ...
}
}
I haven't found a way to do anything similar and am assuming that architecrure re-thinking is needed. But anyway, is there something I've missed?
Any advice on dealing with such situations is appreciated! Thank you!
One possibility is to add an associatedType to RegularProtocol:
protocol PAT {
associatedtype T
func provide() -> T
}
protocol RegularProtocol {
associatedtype T
func consume<P: PAT>(_ pat: P) -> T where P.T == T
}
struct Consumer: RegularProtocol {
typealias T = Int
func consume<P: PAT>(_ pat: P) -> T where P.T == T {
return pat.provide() * 10
}
}
Note that RegularProtocol without associated type would have to accept all PAT types, therefore you cannot implement it partially just for some types.
what is a difference between using generic with function or using associatedType in swift protocols?
protocol Repository {
associatedtype T
func add(data : T) -> Bool
}
and
protocol Repository {
func add<T>(data : T) -> Bool
}
Defined associated type makes classes which conform protocol strong typed. This provide compile-time error handling.
In other hand, generic type makes classes which conform protocol more flexible.
For example:
protocol AssociatedRepository {
associatedtype T
func add(data : T) -> Bool
}
protocol GenericRepository {
func add<T>(data : T) -> Bool
}
class A: GenericRepository {
func add<T>(data : T) -> Bool {
return true
}
}
class B: AssociatedRepository {
typealias T = UIViewController
func add(data : T) -> Bool {
return true
}
}
class A could put any class into add(data:) function, so you need to makes your sure that function handle all cases.
A().add(data: UIView())
A().add(data: UIViewController())
both would be valid
But for class B you will get compile-time error when you will try to put anything except UIViewController
B().add(data: UIView()) // compile-time error here
B().add(data: UIViewController())
An associatedtype is a static type in struct/class which adopts the protocol either via a typealias declaration or via type inference. The type is always the same for that class.
A generic can be anything, even different types in the same class.
This case
protocol Repository {
func add<T>(data : T) -> Bool
}
Is understood by compiler like: "Any type that is fed to the func add will be acceptable and the result of the function will be Bool"
But this
protocol Repository {
associatedtype T
func add(data : T) -> Bool
}
is understood by compiler like: "func add will accept only the type that is defined in the typealias T = ... and return Bool"
In the second case you restrict generic parameters only to the typealiased types.
Another important feature shows up when you use generic parameters in multiple functions of the protocol. In that case it guarantees that func add<T> and func multiply<T> will have the same type T. In case of generic functions it is not guaranteed.
protocol Calculable {
associatedtype T
func add<T>(a: T, b: T) -> T
func multiply<T>(a: T, b: T) -> T
}
// In this case you know that T is the same for all functions
protocol CalculableDifferent {
func add<T>(a: T, b: T) -> T
func multiply<T>(a: T, b: T) -> T
}
// In this case add can accept One type, when multiply can accept Another
I would like to create protocol of types that can be mapped over.
Lets say my goal is to write a operator for map. I realize this will not compile
func <^> <A,B,C>(f: (B) -> C, a: A<B>) -> A<C> {
return a.map(f)
}
Now I would like to replace A with a protocol such that any object that has the map function can be used with this operator.
This is not allowed but what I would like to say is
protocol Maps {
associatedtype E
func map<T>(f: (E) -> T) -> Self<T>
}
The closest I can get to something that makes sense is as follows. However it does not work correctly.
protocol Maps {
associatedtype E
func map<T, U: Maps>(f: (E) throws -> T) rethrows -> U where U.E == T
}
extension Array: Maps {
func map<T, U>(f: (E) throws -> T) rethrows -> U where U : Maps, U.E == T {
return try self.map(transform)
}
}
func <^> <A,B,C,D>(f: (B) -> C, a: A) -> D where A: Maps, D: Maps, A.E == B, D.E == C {
return a.map(f)
}
The main problem here being is that the map function does not return an Array<T> but a Maps protocol where E is T.
Should I just break down and write a <^> operator for every custom struct that can be mapped over? Any suggestions will be appreciated! Thanks
How can I indicate that B.Generator.Element should be A?
protocol SomeProtocol {
typealias A
typealias B: CollectionType
func f(a: A) -> B
}
I realise that I can do func f(node: B.Generator.Element) -> B and eliminate A altogether, but I can't do this if A is defined in some other protocol and SomeProtocol inherits from it.
Edit:
Added example
protocol P {
typealias A
}
protocol Q: P {
typealias B: CollectionType
typealias A = B.Generator.Element
func f(node: A) -> B
}
func g<A, T: Q where A == T.A>(arg1: T, arg2: A) {
let collection = arg1.f(arg2)
collection.contains(arg2) // Error here
}
Edit 2:
To clarify, I am looking to somehow specify A == B.Generator.Element in the protocol itself since I must use a free function. I found a thread in the developer forums with precisely my problem. Looks like its a limitation in the current type system. I've filed a radar, let's hope it gets resolved :)
Edit 3:
A radar already exists for the issue. Use rdar://21420236 while filing.
How about:
protocol SomeProtocol {
typealias A = B.Generator.Element
typealias B: CollectionType
func f(a: A) -> B
}
Edit:
The error you're getting is because there's no guarantee A (B.Generator.Element) conforms to Equatable. Therefore you can't call contains on collection since contains is defined as:
extension SequenceType where Generator.Element : Equatable {
public func contains(element: Self.Generator.Element) -> Bool
}
(I'm not sure why Xcode is presenting contains as a valid method to call, could be a bug...)
To solve this you can do:
// Since you're using Swift 2 you could use a protocol extension instead of a free function.
// Unfortunately, Xcode complains without the explicit declaration that `A` is
// `B.Generator.Element`.
extension Q where A: Equatable, A == B.Generator.Element {
func g(elem: A) {
let collection = f(elem)
collection.contains(elem)
...
}
}