Protocol restriction on another protocol associated type - swift

I have a protocol MyProtocol which has an associatedtype, inferred by the return type of myFunction
struct MyStruct: Codable {
var myVar: Int
}
protocol MyProtocol {
associatedtype MyType
func myFunction()-> MyType
}
class MyClass1: MyProtocol {
func myFunction()-> MyStruct? {
return MyStruct(myVar: 1) //ex.
}
}
class MyClass2: MyProtocol {
func myFunction()-> Int? {
return 1 // ex.
}
}
In MyClass1, associatedtype is infered as MyStruct?
In MyClass2, associatedtype is infered as Int?
from there everything works fine. No I want to build another protocol that can only be applied to a MyProtocol if associatedtype is Codable :
protocol MyProtocolCodable where Self: MyProtocol, MyType == Codable? {}
the code runs fine until here but when I try to apply it to my class I get an error:
extension MyClass1: MyProtocolCodable{} 🛑
'MyProtocolCodable' requires the types 'MyStruct?' and 'Codable?' (aka 'Optional<Decodable & Encodable>') be equivalent
yet, as far as I can see MyStruct? (aka Optional) and Codable? (aka Optional<Decodable & Encodable>) are equivalent?
How can I get rid of this error message? Am I doing something that is not meant to be done?

As far as I can see MyStruct? (aka Optional) and Codable? (aka
Optional<Decodable & Encodable>) are equivalent?
No, they are not. MyStruct conforms to Codable but is not equivalent of it.
As in: every MyStruct is Codable but not every Codable is MyStruct.
You can try changing MyType == Codable? to MyType: Codable:
protocol MyProtocolCodable where Self: MyProtocol, MyType: Codable {}
as MyStruct is not equal to Codable?.

Related

Difference between protocol combining typealias and empty conforming protocol

Is there a difference between these two in Swift?
protocol ABProtocol: AProtocol, BProtocol {}
typealias ABProtocol = AProtocol&BProtocol
To make things clearer, I will rename the second one to:
typealias ABProtocolIntersection = AProtocol & BProtocol
I can think of two differences off the top of my head.
If your type conform to AProtocol and BProtocol, the type is automatically a subtype of ABProtocolIntersection, but it does not automatically conform to ABProtocol. After all, ABProtocol is a totally different protocol.
Example:
class Foo: AProtocol, BProtocol { ... }
func foo<T: ABProtocolIntersection>(type: T.Type) { }
func bar<T: ABProtocol>(type: T.Type) { }
foo(type: Foo.self) // works
bar(type: Foo.self) // error
Another difference is that you can put extensions on ABProtocol, but not ABProtocolIntersection:
extension ABProtocol { } // OK
extension ABProtocolExtension { } // error
This is because ABProtocolIntersection is a non-nominal type, similar to types like (Int, Int) or (Int) -> String. See also: What is a 'non-nominal type' in Swift?
Yes there is a difference. The former defines a new protocol to which types must conform when it is used. The latter only defines a "placeholder" for AProtocol&BProtocol
Consider the following code:
protocol AProtocol{}
protocol BProtocol{}
protocol ABProtocol1: AProtocol, BProtocol {}
typealias ABProtocol2 = AProtocol & BProtocol
func f1(value: ABProtocol1) {}
func f2(value: ABProtocol2) {}
Arguments to f1 must conform to ABProtocol1 but arguments to f2 can conform to AProtocol and BProtocol. You do not need to explicitly conform types to ABProtocol2. For example:
struct A: AProtocol, BProtocol
{
}
f1(value: A()) // Error!
f2(value: A()) // OK

Decodable conformance with other protocols in Swift

The issue is when a struct conforms to a protocol (let's call it PA) and Decodable, but PA imposes a property with a type that is not Decodable. Example:
protocol PA {
var b: [PB]? { get }
}
protocol PB {}
struct SA: PA, Decodable {
let b: [PB]? // SA's conformance to Decodable wants this to be [Decodable], but PA's conformance imposes [PB]
}
struct SB: PB, Decodable {}
the code above refuses to compile, with:
error: type 'SA' does not conform to protocol 'Decodable'
note: cannot automatically synthesize 'Decodable' because '[PB]?' does not conform to 'Decodable'
Changing that line to:
let b: [PB & Decodable]?
does not work either and gives:
error: type 'SA' does not conform to protocol 'PA'
note: candidate has non-matching type '[Decodable & PB]?'
error: type 'SA' does not conform to protocol 'Decodable'
note: cannot automatically synthesize 'Decodable' because '[Decodable & PB]?' does not conform to 'Decodable'
note: protocol requires property 'b' with type '[PB]?'; do you want to add a stub?
Note that the 4th line is non-sense: "'[Decodable & PB]?' does not conform to 'Decodable'". Wait what?
Any suggestion?
You may create a mixed protocol:
protocol PADecodable {
var b: [PB & Decodable]? { get }
}
struct SA: PADecodable {
let b: [PB & Decodable]?
}
you can fix it by:
protocol PA {
var b: [PB]? { get }
}
protocol PB {}
struct SA<T: PB & Codable>: PA, Codable {
private var _b: [T]?
var b: [PB]? {
return _b
}
}

Class-only protocol as typealias for associatedtype with AnyObject constraints

In Swift 4.0 I could write something like this
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: AnyObject
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}
But in Swift 4.1 I receive the error Type 'StringManager' does not conform to protocol 'ObserversHolder'.
Is it possible to resolve this?
Change AnyObject to Any
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: Any
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}

How do you structure generic type protocol conformance in Swift?

The following contrived Swift 2 example from real-world code won't compile:
protocol SomeModelType { }
protocol SomeProtocol {
var someVar: SomeModelType? { get }
}
class ConcreteClass<T: SomeModelType>: SomeProtocol {
var someVar: T?
}
This doesn't make sense to me fully. I would assume in ConcreteClass that because I have T being constrained to SomeModelType and have T as the backing type for the someVar property, the compiler would be able to figure out that the SomeProtocol was being conformed to by ConcreteClass.
How should an example like this be structured? Is it possible to the Swift compiler to determine protocol conformance through generic type constraints?
protocol SomeModelType { }
protocol SomeProtocol {
associatedtype T: Any
var someVar: T? { get }
}
class ConcreteClass<T> :SomeProtocol where T: SomeModelType {
var someVar: T?
}

protocol associated type typealias assignment compile error

Following code:
protocol SomeProtocol {
typealias SomeType = Int // used typealias-assignment
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) {
print(someVar)
}
}
gives compile-time error:
Use of undeclared type 'SomeType'
Adding, say typealias SomeType = Double, to the SomeClass resolves the error.
The question is, what's the point of typealias-assignment part (which is optional btw) of protocol associated type declaration though?
In this case the assignment of Int to the typealias is equal to no assignment because it gets overridden by your conforming type:
// this declaration is equal since you HAVE TO provide the type for SomeType
protocol SomeProtocol {
typealias SomeType
func someFunc(someVar: SomeType)
}
Such an assignment provides a default type for SomeType which gets overridden by your implementation in SomeClass, but it is especially useful for protocol extensions:
protocol Returnable {
typealias T = Int // T is by default of type Int
func returnValue(value: T) -> T
}
extension Returnable {
func returnValue(value: T) -> T {
return value
}
}
struct AStruct: Returnable {}
AStruct().returnValue(3) // default signature: Int -> Int
You get the function for free only by conforming to the protocol without specifying the type of T. If you want to set your own type write typealias T = String // or any other type in the struct body.
Some additional notes about the provided code example
You solved the problem because you made it explicit which type the parameter has. Swift also infers your used type:
class SomeClass: SomeProtocol {
func someFunc(someVar: Double) {
print(someVar)
}
}
So SomeType of the protocol is inferred to be Double.
Another example where you can see that SomeType in the class declaration doesn't refer to to the protocol:
class SomeClass: SomeProtocol {
typealias Some = Int
func someFunc(someVar: Some) {
print(someVar)
}
}
// check the type of SomeType of the protocol
// dynamicType returns the current type and SomeType is a property of it
SomeClass().dynamicType.SomeType.self // Int.Type
// SomeType gets inferred form the function signature
However if you do something like that:
protocol SomeProtocol {
typealias SomeType: SomeProtocol
func someFunc(someVar: SomeType)
}
SomeType has to be of type SomeProtocol which can be used for more explicit abstraction and more static code whereas this:
protocol SomeProtocol {
func someFunc(someVar: SomeProtocol)
}
would be dynamically dispatched.
There is some great information in the documentation on "associated types" in protocols.
Their use is abundant throughout the standard library, for an example reference the SequenceType protocol, which declares a typealias for Generator (and specifies that it conforms to GeneratorType). This allows the protocol declaration to refer to that aliased type.
In your case, where you used typealias SomeType = Int, perhaps what you meant was "I want SomeType to be constrained to Integer-like behavior because my protocol methods will depend on that constraint" - in which case, you may want to use typealias SomeType: IntegerType in your protocol, and then in your class go on to assign a type to that alias which conforms to IntegerType.
UPDATE
After opening a bug w/ Apple on this and having had extensive discussion around it, I have come to an understanding of what the base issue is at the heart of this:
when conforming to a protocol, you cannot directly refer to an associated type that was declared only within that protocol
(note, however, that when extending a protocol the associated type is available, as you would expect)
So in your initial code example:
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
class SomeClass: SomeProtocol {
func someFunc(someVar: SomeType) { // use of undeclared type "SomeType"
print(someVar)
}
}
...the error re: "use of undeclared type" is correct, your class SomeClass has not declared the type SomeType
However, an extension to SomeProtocol has access to the associated type, and can refer to it when providing an implementation:
(note that this requires using a where clause in order to define the requirement on the associated type)
protocol SomeProtocol {
typealias SomeType = Int
func someFunc(someVar: SomeType)
}
extension SomeProtocol where SomeType == Int {
func someFunc(someVar: SomeType) {
print("1 + \(someVar) = \(1 + someVar)")
}
}
class SomeClass: SomeProtocol {}
SomeClass().someFunc(3) // => "1 + 3 = 4"
There is great article that actually gives you answer for your question. I suggest everyone to read it to get into type-aliases and some more advanced stuff that comes up when you use it.
Citation from website:
Conceptually, there is no generic protocols in Swift. But by using
typealias we can declare a required alias for another type.