error: protocol 'Protocol' requirement 'instance' cannot be satisfied by a non-final class ('Class') because it uses 'Self' in a non-parameter, non-result type position
protocol Protocol {
var instance: Self {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Here is how I would express what I want, in C#. (C# does not, to my knowledge, have a way to enforce that the generic parameter "Self" is actually the Self we know from Swift, but it functions well enough as documentation that should make me do the right thing.)
interface Protocol<Self> where Self: Protocol<Self> {
Self instance {get;}
}
class Class: Protocol<Class> {
public Class instance {get {return new Subclass();}}
}
class Subclass: Class {}
…how that might look in a future version of Swift:
protocol Protocol {
typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf
var instance: FinalSelf {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
How I'm emulating the portion of that which is relevant to my problem:
protocol Protocol: ProtocolInstance {
static var instance: ProtocolInstance {get}
}
protocol ProtocolInstance {}
class Class: Protocol {
static var instance: ProtocolInstance {return Subclass()}
}
class Subclass: Class {}
And, here is what I believe to be the relevant portion of my code:
protocol Protocol {
static var 🎁: Self? {get} // an existing instance?
static var 🐥: Self {get} // a new instance
func instanceFunc()
}
extension Protocol {
static func staticFunc() {
(🎁 ?? 🐥).instanceFunc()
}
}
As it says, you can't do this, and for good reason. You can't prove you'll keep your promise. Consider this:
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
So x
should be AnotherSubclass
according to your protocol (that's Self
). But it'll actually be Subclass
, which is a completely different type. You can't resolve this paradox unless the class is final
. This isn't a Swift limitation. This limitation would exist in any correct type system because it allows an type contradiction.
On the other hand, something you can do is promise that instance
returns some consistent type across all subclasses (i.e. the superclass). You do that with an associated type:
protocol Protocol {
typealias InstanceType
var instance: InstanceType {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
Now x
is unambiguously of type Class
. (It also happens to be random other subclass, which is kind of weird, but that's what the code says.)
BTW, all of this usually suggests that you're using subclassing when you really shouldn't be. Composition and protocols would probably solve this problem better in Swift. Ask yourself if there's any reason that Subclass
needs to actually be a subclass of Class
. Could it be an independent type that conforms to the same protocol? All kinds of problems go away when you get rid of subclasses and focus on protocols.
I've been thinking about this more, and there may be a way to get what you're looking for. Rather than saying that all subclasses implement instance
, attach instance
as an extension. You can still override that if you want to return something else.
protocol Protocol {
init()
}
class Class: Protocol {
required init() {}
var instance: Class { return Subclass() }
}
extension Protocol {
var instance: Self { return self.dynamicType.init() }
}
class Subclass: Class {}
This dodges the inheritance problem (you can't create the same "AnotherClass
returning the wrong type" this way).
This would actually make sense and work if you don't want to actually return Self
for every subclass like this:
protocol Protocol : class {
typealias Sub : Self
var instance: Sub {get}
}
Which means that your protocol defines a typealias that has to be a subclass of itself. The following code would just work:
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Class().instance // Returns SubClass()
However the code above doesn't compile with the error
error: inheritance from non-protocol, non-class type '`Self`'
which I think is a bug, because Self
is declared as a class type. You can however make it somehow work like this:
protocol Protocol : class {
typealias Sub : Class
var instance: Sub {get}
}
but then you don't have much of the protocol itself because only the class itself should ever conform to it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With