Specify associated type of parent protocol in child protocol with Swift - swift

In Swift 2, I have a protocol:
protocol Protocol {
typealias Type
}
When I want to use Protocol without defining what type to use for Type:
var protocol1: Protocol
Then I'm getting the following error:
Protocol 'Protocol' can only be used as a generic constraint because it has Self or associated type requirements
It is clear why this is not going to work.
I have another protocol, which inherits from the first protocol and specifies that the associated type Type should be a String.
protocol AnotherProtocol: Protocol {
typealias Type = String
}
The same error occurs, when I try to use this protocol:
var protocol2: AnotherProtocol
Protocol 'AnotherProtocol' can only be used as a generic constraint because it has Self or associated type requirements
Why am I getting an error for this, although I have specified the associated type?
Is there another way for the second protocol to specify the associated type of the parent protocol, without having to specify it again in every class that implements the second protocol?

Your error isn't coming from the declaration or definition of your protocols, it comes from how you're trying to use them. There are two basic ways you can use a protocol: as a pseudo-type, or as a constraint. When you declare a variable like this:
var protocol1: Protocol
You're using the protocol like it's a type: the type of protocol1 is Protocol. This is different from using it as a constraint. Other types conform to the protocol if it's used as a constraint:
struct SomeStruct: Protocol {...
Now, you can use your protocols in either way, but there are some disadvantages to both ways. Firstly, you can't store a heterogenous collection of "protocols as a constraint", whereas you can when you use protocols as a type.
Secondly, if there are requirements on self or associated types, you can no longer use that protocol as a type.
So, here's what I think you're looking for. Your first protocol:
protocol Protocol {
typealias Type
}
And then the second:
protocol InheritingProtocol {
typealias Type = String
}
Now, if you want something to conform to the second protocol, it must be used as a constraint. That means that some type conforms to the protocol, and then you can have some instance of that type.
struct SomeType : InheritingProtocol {}
let someInstance = SomeType()

Related

Is it possible in Swift to require an associated type to conform to an associated protocol of an associated type?

I am attempting (for essentially no reason) to make a protocol which describes categories from category theory. I have tried to come up with something like this.
protocol Category {
associatedtype Object: Protocol
}
protocol Hom {
associatedtype C: Category
associatedtype Source: C.Object
associatedtype Target: C.Object
}
In particular, I want each Hom type to have an associated category C and an associated Source and Target type which are both objects in that category. Consequently, I have associated an Object protocol to each Category, and have tried to make the Source and Target of a Hom conform to the Object protocol for the corresponding Category. The code above fails to compile with
Type 'Self.Source' constrained to non-protocol, non-class type 'Self.C.Object'
This error is at least unclear, for C.Object is declared as a protocol. Is there any way I can work around this issue?
Edit:
As Rob has pointed out, the code as is does not make much sense. Protocol is a particular class from ObjC and is not a type which describes protocols. Further, there is no type that describes all protocols, because protocols themselves cannot conform to protocols as they are just requirements on other types. What I was looking for is a metatype which Any.Protocol, Sequence.Protocol, etc. were all instances of.
I will go into more detail as to what kind of construction I am trying to describe.
A category is a type Object and a type of homomorphisms between every pair of instances of Object. For two instances of Object, A and B, the type of homomorphisms is generally written as Hom(A,B), but I will write Hom<A,B> to be Swiftier. Categories then come equipped with composition which has the signature <A: Object, B: Object, C: Object>(_ f: Hom<A,B>, _ g: Hom<B,C>) -> Hom<A,C>.
If f is an instance of Hom<A,B>, then A is called the source or domain of f and B is called the target or codomain of f.
Types themselves are then a category where Object is the metatype of all types and Hom<A,B> = (A) -> B.
The primary reason categories are difficult in Swift is because Swift does not have dependent types. There is no way to describe a category whose object type is Int because there is no way to have a type Hom<0,0>. However, if the Object type is required to be a metatype, then suddenly Hom<A,B> is a sensical thing to describe to the type system because an instance of a metatype is a type (I think), which can be a generic parameter. This is what I attempted to describe by setting Object: Protocol.
In Swift, I would really wish to describe
protocol Category {
associatedtype Object: Metatype
associatedtype Hom<A: Object, B: Object>
func compose<A: Object, B: Object, C: Object>(_ f: Hom<A,B>, then g: Hom<B,C>) -> Hom<A,C>
}
but this is also a non-starter because associated types cannot have generic parameters.
In my use case, I have a protocol that describes finitely generated abelian groups and a protocol that describes finitely generated unital rings, and I would love to write generic code which does not care whether or not it is working with GroupHom<A,B> where A: AbelianGroup, B: Abelian Group, RingHom<A,B> where A: Ring, B: Ring, or (A) -> B as each of these come equipped with the correct kind of composition.
It might just be impossible to do this, which I would be willing to accept. Please let me know if this is different enough that it should be asked as a separate question.
associatedtype Object: Protocol
This line doesn't mean what you think it means. Protocol isn't part of Swift. It's part of the ObjC runtime. (It's a really confusing import.)
But even if it did mean that, I don't believe it would help you. It is important to realize that in Swift, protocols do not conform to protocols, and protocols with associated types are not types. They're constraints on types. This tends to creep in all over the place and surprise people.
My suspicion is that you're looking to model things along these lines (note that my category theory is very weak, so please forgive me if I use the wrong terms at points).
We want to get to a point where we can say "a category has a collection of objects and a collection of arrows between those objects." To get there, I think we'd want to start with a generic Arrow:
protocol Arrow {
associatedtype Source
associatedtype Target
var apply: (Source) -> Target { get }
}
A homomorphism is an arrow that maps back to its own type.
protocol Homomorphism: Arrow where Source == Target {
typealias Object = Source
}
And with that, we can express a Category:
protocol Category {
associatedtype Object
associatedtype Arrow: Homomorphism where Arrow.Object == Object
}
I'd like to talk about the category of integers and functions (I believe that's a proper category). So first I need functions.
struct Function<Source, Target>: Arrow {
let apply: (Source) -> Target
}
extension Function: Homomorphism where Source == Target {}
And then I can declare the category.
struct Integers: Category {
typealias Object = Int
typealias Arrow = Function<Int, Int>
}
And create a morphism.
let increment = Function(apply: { (x: Int) in x + 1 })
I think this is somewhat the direction you're looking for.
dg
In swift you cannot use a protocol associatedtype as an associatedType cause it is not defined what type is.
Associated Type can only be used as a constraint for types, like this:
protocol Category {
associatedtype Object1:Equatable
}
class Homs:Category{
typealias Object1 = Int
func sum(element:Object1){
print(element+element)
}
}
Or like this
protocol Category {
associatedtype Object1: Equatable
}
protocol Homs {
associatedtype Cat:Category
associatedtype Source: Category where Source.Object1 == Cat.Object1
associatedtype Target: Category where Target.Object1 == Cat.Object1
}
The reason of why your code not compile, is cause, your associatedtype Target is constrained to implements a associatedType (that is not defined) of a protocol. Someone need to define it before to use as a constraint.
An approach to resolve your problem clould be, generate a generic class. Let's to see it:
protocol Category {
associatedtype Object1: Equatable
}
class Homs<Cat:Category,Source,Target> where Cat.Object1 == Source && Cat.Object1 == Target.Object1{
}
Another approach could be create a generic Category class, and a protocol with type for Category class, category Class, Source and Target conforming to Type:
class Category<T>{
}
protocol Homs {
associatedtype ObjectType
associatedtype Cat:Category<ObjectType>
associatedtype Source where Source == ObjectType
associatedtype Target where Target == ObjectType
}
O something like the second example:
protocol Category {
associatedtype Object1: Equatable
}
protocol Homs {
associatedtype Cat:Category
associatedtype Source: Category where Source.Object1 == Cat.Object1
associatedtype Target: Category where Target.Object1 == Cat.Object1
}
Remember, You cannot use protocols with associated types as a type constraint for associated type or as a type for a variable or constant, first someone need to define the associated types.
I hope I helped you.

Validate conformance object to protocol with associated type

This is my code:
protocol Protocol {
associatedtype A
}
class Class: Protocol {
typealias A = Int
}
assert(type(of: Class()) == Protocol.self)
The error is ofcourse:
Protocol 'Protocol' can only be used as a generic constraint because
it has Self or associated type requirements
I understand the error, but I hope there is some way to check if a given object conform to a protocol with an associated type, without the need of binding/casting.

Make generic type Type<T> conform to a protocol only if T conforms to that protocol

I'm trying to extend the Optional type to make it DataConvertible (a protocol that I have declared somewhere else in my code) by adding protocol conformance to it only if the Wrapped type conforms to the same protocol.
I tried this:
extension Optional: DataConvertible where Wrapped == DataConvertible { ... }
but then I get the error:
Extension of type 'Optional' with constraints cannot have an inheritance clause
Is there any way to achieve this with the current version of Swift?

Swift protocol used as type in method not accepting instances of same protocol

I've encountered a problem with a method which I cannot explain. Here is some test code which shows the problem:
protocol Base {}
protocol Extended: Base {}
struct Instance:Extended {}
let anInstance = Instance()
let instanceOfBase = anInstance as Base
let instanceOfExtended = anInstance as Extended
func aMethod<T:Base>(_ instance:T) {}
aMethod(anInstance)
aMethod(instanceOfBase) // Error - Cannot invoke 'aMethod' with an argument list of type '(Base)'
aMethod(instanceOfExtended) // Error - Cannot invoke 'aMethod' with an argument list of type '(Extended)'
According to the Apple doco I've read on protocols, generics, etc. aMethod() should accept any object that conforms to the Base protocol. Yet it rejects both instances where I have cast them to either Base or Extended.
Can anyone explain this?
Also:
func aMethod2(_ instance:Base) {}
aMethod2(anInstance)
aMethod2(instanceOfBase)
aMethod2(instanceOfExtended)
Works fine so the difference seems to be whether the instance argument is based (excuse the pun) on Base or <T:Base>.
For anyone questioning why I would declare a generic here. The original code looked like this:
func addViewController<T:ModelObject>(_ stack:inout [UIViewController],
object:T?,
controller:DetailsViewController<T>?,
storyboardId:String) {...
As you can see, I want to constrain several arguments to the same type. Hence the use of a generic rather than just specifying base.
T is a constraint on a concrete type
<T:Base> is a constraint on the placeholder type T. T must be a concrete type (a class, enum or struct such as Instance) that conforms to the Base protocol. The placeholder type T cannot be the Base protocol.
aMethod<T:Base>(:) must be called with a variable that is of a type that conforms to the Base protocol at compile time. aMethod<T:Base>(:) cannot be called with a variable that is only known to be of type Base.
The following line of code instantiates a variable named anInstance that is of struct type Instance.
let anInstance = Instance()
aMethod(anInstance) compiles because anInstance is of a concrete type Instance that conforms to the Base protocol.
The following line of code instantiates a variable named instanceOfBase that is of protocol type Base.
let instanceOfBase: Instance = anInstance as Base
aMethod(instanceOfBase) does not compile because instanceOfBase is not of a concrete type that conforms to the Base protocol. It is of the protocol type Base.
Here is another snippet that fails to illustrate the issue. In this case the base argument is only known to be of the protocol type Base.
func aMethod(base: Base) {
aMethod(base) // Cannot invoke 'aMethod' with an argument list of type '(Base)'
}
Hmm, I don't really get why you're doing this.
Let's say:
you want aMethod to accept any instance that conforms to Base protocol, then you can just make it func aMethod(_ instance: Base) {}
You want preserve type information inside the function then you use the generic method but pass in an instance of a concrete implementation of the protocol.
Please note that func aMethod<T:Base>(_ instance:T) {} means it's expecting an instance of type T that conforms Base. Neither Base or Extended is a valid type.
What you're doing here is using generics without its power...Which, IMHO, doesn't make sense. Unless you can provide a more realistic scenario?

How to pass protocol with associated type (generic protocol) as parameter in Swift?

I have to pass an interface as a parameter to a function. Interface is generic a.k.a. has a associated type. I couldn't find a good way to do that. Here is my code:
protocol IObserver : class {
typealias DelegateT
...
}
class Observer: IObserver {
typealias DelegateT = IGeneralEventsDelegate // IGeneralEventsDelegate is a protocol
...
}
func notify(observer: IObserver) { ... } // here I need a type for observer param
I found that this will work:
func notify<T: IObserver where T.DelegateT == IGeneralEventsDelegate>(observer: T) { ... }
, but come on that is too complicated. What if I want to save this param in class variable, should I make the whole class generic, just because of this function.
It is true that I'm C++ developer and I'm new to the Swift language, but the way the things are done are far too complicated and user unfriendly ... or I'm too stupid :)
If you use typealias in a protocol to make it generic-like, then you cannot use it as a variable type until the associated type is resolved. As you have probably experienced, using a protocol with associated type to define a variable (or function parameter) results in a compilation error:
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self os associated type requirements
That means you cannot use it as a concrete type.
So the only 2 ways I am aware of to use a protocol with associated type as a concrete type are:
indirectly, by creating a class that implements it. Probably not what you have planned to do
making explicit the associated type like you did in your func
See also related answer https://stackoverflow.com/a/26271483/148357