Denis
(Denis Poifol)
1
Currently the following piece of code does not compile :
protocol P {}
struct A: P {}
protocol Q {
var p: P { get }
}
struct B: Q {
var p = A()
}
The compiler raise the error: Type 'B' does not conform to protocol 'Q'
One way to workAround this problem is to declare a private variable of type A and for the variable p to dynamically return a
struct B: Q {
private var a = A()
var p: P { return a }
}
It would be awesome if the compiler could recognize A implements P and recognize the first writting.
1 Like
Explicitly writing the type var p: P = A() will do for now.
1 Like
Denis
(Denis Poifol)
3
It would enable to satisfy the compiler but it would not enable to use p as an instance of type A
It can of course be used by casting to A each time we need it, but it would be great if the compiler handle all this by unederstanding A is implementing P
That is because you specified P as the concrete type of the variable. Writing
protocol P { ... }
struct Foo: P { ... }
protocol P1 {
var foo: P { get }
}
doesn't mean you can conform to P1 like this:
class Foo1: P1 {
var foo: AnyTypeConformingToP = ...
}
since var p isn't generic. It's of type P.
P.S.
if you want to do it that way, you have to go "generic":
protocol P {}
struct A: P {}
protocol Q {
associatedtype Type1: P
var p: Type1 { get }
}
struct B: Q {
typealias Type1 = A
var p = A() // no problem
}
2 Likes
Denis
(Denis Poifol)
5
Maybe I am missing the obvious but what prevents the compiler to understand A is implmenting P kind of like polymorphism.
Your solution using protocol with associated type would work but defeat the purpose of anonymising B with a protocol since B cannot be casted as Q which "Can only be used a type constraint"
It could be stored using type erasure but we would have to specify the type of A and thus the variable could not be anonymised by the protocol P.
Maybe at this point there is another work around to anonymise the type A but it's getting awfully complicated for something that should be that much don't you agree?
If you're asking why the compiler doesn't infer var p = A() as P, it simply isn't smart enough.
You can anonymize B if you don't need to store it as Q.
Nevin
7
Because Q declares “Any type conforming to me, will have a property p whose type is an existential that can store any value conforming to P.”
Suppose we make a protocol R that refines Q to require p is mutable:
protocol R: Q {
var p: P { get set }
}
Then we can write an extension method on R that sets p to something:
struct C: P {}
extension R {
mutating func foo() {
p = C()
}
}
And now, to bring it all together, we can retroactively conform B to R:
extension B: R {}
This all works exactly as it says, and it means we can set the value of B.p to an instance of C.
1 Like
Denis
(Denis Poifol)
8
It much clearer to me why it's not possible.
Thanks a lot for taking the time of explaining it to me.
This is a reasonable in request. In general, it makes sense for the witness to be a subtype of the protocol requirement. For example, if the protocol requires a method returning Any, it makes sense for the conforming type to provide a method returning Int, since every method returning Int is also a method returning Any, etc.
The canonical JIRA bug for this is [SR-522] Protocol funcs cannot have covariant returns · Issue #43139 · apple/swift · GitHub. A contributor started working on implementing this, but unfortunately it was abandoned: https://github.com/apple/swift/pull/8718
If someone is interested in dusting this off though, it should probably go through evolution discussion, and would need to be gated on -swift-version <N> since it's source breaking. There are also some subtle issues to resolve, for example if multiple witnesses match a single requirement, how do you rank them? Or what if a default in an extension matches exactly, but another method in the type itself is an inexact match?
Slava
If B defines a property whose type is a subtype of P, then B cannot conform to R, because the property requirement is mutable. However for an immutable property requirement there is no such difficulty, and the conformance could be allowed.
2 Likes
It does make sense, but it violates Liskov substitution.
protocol P {
var foo: UIView { get set }
}
class A: P {
var foo: UIButton = UIButton()
}
let a = A()
var p: P = a
p.foo = UIView() // or UIDatePickerView for instance
a.foo.(...) ?
DeFrenZ
(Davide De Franceschi)
12
It's fine if it's a get-only though ;)
4 Likes
If the property is settable, sure. If the property requirement in the protocol is read-only, its equivalent to a method returning a value of that type.
Denis
(Denis Poifol)
14
Hello @Slava_Pestov I would like to give it a try but unfortunatelly the closest I've come to C++ is programming in C and I am not really familar with the swift compiler project, however I am quite tenacious, if you could pinpoint me to something that could help me understand how the compiler handle this issue, I will gladly give it a go.