Swift - Differences between inheriting protocol and constraining Self to protocol - swift

I have a protocol A:
protocol A { }
What are the differences between implementing protocol B like this:
protocol B: A { }
versus implementing it like this:
protocol B where Self: A { }
?
What can I do with one that I can't do with the other? Where will I run into a problem with one that I could fix by switching it to the other?

One difference is in how you conform to the protocol.
In the former case, a conformance to B implies a conformance to A.
struct BImpl: B {} // `A` is implied here
In the latter case, a conformance to B requires a conformance to A, but it doesn't implicitly create one:
struct BImpl: A, B {} // `A` is explicitly required here

Related

Why am I getting this conformance problem?

protocol A {}
protocol B: A {}
protocol C {
var X: A { get }
}
struct D: C {
let X: B // ERROR: does not conform to protocol "C"
}
Shouldn't this be okay since B conforms with A?
I tried to remedy it by using an associatedtype in protocol C:
protocol A {}
protocol B: A {}
protocol C {
associatedtype SomeType: A
var X: SomeType { get }
}
struct D: C {
let X: B // ERROR: does not conform to protocol C -- seemingly requiring a concrete type
}
The error makes sense if you think about it the following way.
/// It does not have any requirements
protocol A {}
/// It has to satisfy all of the requirements of A (whatever they are)
/// It does not have any of it's own requirements (currently)
/// It is expected to add some additional requirements of it's own
/// Otherwise it defeats the purpose of having this
protocol B: A {}
/// This has only one requirement, X that conforms to A
protocol C {
var X: A { get }
}
/// This has only one requirement, X that conforms to B
/// The problem here is
/// - A & B are different and can not be used interchangeably
/// (at least in the current versions)
struct D: C {
let X: B // ERROR: does not conform to protocol "C"
}
Logically it makes sense - that B has to satisfy all requirements of A and hence it should be allowed.
I think Swift Forums might be a better place for discussing this.

Why is it not possible to constraint associatedtype with a protocol inheritance?

I know that in a situation where a protocol is used with associated value it is usually required to do type erasure. But sometimes it seems overkill. For example the situation where we know that some of classes will have associated type set to Any we could make a protocol which makes the constraint with where clause. Like in the following example:
protocol A {
associatedtype B
func a(b: B)
}
protocol C: A where B == Any {}
func runC(_ c: C) { // Protocol 'C' can only be used as a generic constraint because it has Self or associated type requirements
c.a(b: Data())
}
class CImpl: C {
func a(b: B) {
print(b)
}
}
runC(CImpl())
Why does it still complains about constraints on the func runC when it has one in protocol C? Is there a way to make it work without type erasure boilerplate?

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.

Distinguishing between inherited `associatedtype`s in Swift protocols

I'm working with a library which defines two protocols, A, and B, each with its associatedtype T, like this:
protocol A {
associatedtype T
}
protocol B {
associatedtype T
}
The two protocols are not coupled on T, and so in theory a third protocol could inherit from both A and B, typealiasing each T to a different type. Unfortunately, I can't get Swift to distinguish between the two Ts. I tried something like:
protocol C: A, B {
typealias A.T = String
typealias B.T = String
}
but this is not supported syntax.
Is there a way to get around this?
This has been discussed in Multiple protocols associatedtype name collision in the Swift forum. Xiaodi Wu writes:
It is very much possible, but the identically named associated types must be, in the conforming type, fulfilled by the same type.
In the future, there may be syntax added to allow types to conform to two protocols with such clashing requirements, but it adds great complexity in terms of implementation and is not without its own pitfalls for users (for example, it can get very confusing for end users of your type).
So a type can conform to both A and B with identical associated type T, e.g.
struct Foo: A, B {
typealias T = String
}
and a protocol can inherit from both A and B and restrict T to the identical type:
protocol C: A, B where T == String {
}
Conforming to both protocols with distinct associated types is currently not supported.

Why sub-protocol doesn't fulfill conformance for its parent? [duplicate]

This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 4 years ago.
I have structs that are conforming to protocols, but are using derived protocols, instead of directly using its parent protocol:
protocol A { }
protocol B: A { }
protocol C {
var property: A { get }
}
struct Foo: C {
let property: A
}
struct Bar: C {
let property: B
}
// Error: Type 'Bar' does not conform to protocol 'C'
Why doesn't Bar fulfill the conformance because property is a sub-protocol of A.
B describes a type that conforms to some set of rules. It is not itself a type that conforms to those rules. B does not conform to B, let alone anything it requires additional conformance to. protocol B:A says "anything that conforms to B also must conform to A." However, B does not conform to B, and so does not conform to A.
This has been answered many times on SO, but to repeat the "why," it comes down most simply to this example:
protocol A {
init()
}
func f(type: A.Type) {
let a = type.init()
}
f(type: A.self)
If A itself conforms to A, then this should be legal. But what init should f call? In the presence of init and static requirements, it's not possible for protocols to conform to themselves.
While the specific covariance you want in this case is possible in principle, Swift doesn't have the ability to distinguish between legal and illegal covariance, and disallows all of it. For example, consider the small variation from immutable to mutable:
protocol C {
var property: A { get set }
}
In this case, it is definitely impossible for Bar to conform (it would have to provide a setter for property that accepted any A, even though the getter would return a subtype of A). Swift is not able to distinguish all the cases where it is possible (generally when everything is immutable and there are no init or static requirements) from the cases where it isn't. There's been some discussion of allowing these kinds of cases where it's technically possible, but the concern is that very small changes to the protocol could then break your entire type structure, sometimes for non-obvious reasons. Instead, at least for now, type requirements are generally invariant.
A typical way to address this overall issue is to just provide accessors for the types you want. For example:
protocol A { }
protocol B: A { }
protocol C {
var aProperty: A { get }
}
struct Foo: C {
let aProperty: A
}
struct Bar: C {
var aProperty: A { return bProperty }
let bProperty: B
}
This provides greater flexibility (you can make bProperty a var if you like), and makes your code more explicit.