5

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()
    }
}
  • Looks like a bug — you can make similar constructs work for instance methods but not class methods or static properties. [Have you filed it?](http://bugreport.apple.com) – rickster Oct 07 '15 at 18:17
  • I can't get that to compile either. Edited. –  Oct 07 '15 at 18:20
  • I believe your updated example code from C# is exactly equivalent to my `typealias InstanceType` code. Is there some behavioral difference? If you called `Subclass.instance()` in your C#, wouldn't the return type (not implementation, but variable type) be `Class`? – Rob Napier Oct 08 '15 at 22:05
  • The problem comes elsewhere in the protocol, where I must use the return value of instance. I don't specifically need to express that it's a subclass of Self, but I do need it to at least implement Protocol. The C# code's restrictions make it so convoluted to do something other than return Self or a subclass of it that I don't even know if it's possible. –  Oct 08 '15 at 22:31

2 Answers2

7

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).

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • The promise I want to make is that instance returns either a Self or a T:Self; I apologize if saying "Self" didn't make that clear, but if there is syntax which expresses what I want, I don't know it yet. –  Oct 07 '15 at 20:17
  • You can't keep that promise with classes. See my `AnotherSubclass` example. This isn't a problem of syntax. It's a problem of types. There's no way to express what you're saying without contradiction. If `Class`, `Subclass` and `AnotherSubclass` were all structs (or just didn't have inheritance), then they could all implement `instance` consistently. But when you mix in inheritance, you can't keep your promise anymore. – Rob Napier Oct 07 '15 at 20:18
  • Thanks. I see now that the problem comes from not being able to express that a class implements a protocol without also saying that its subclasses implement that protocol as well. (I knew that, but hadn't encountered a problem resulting from it until now.) I assume this is something that will be remedied in the future, as the concept of protocols evolves. Also, as far as I know, I have to deal with inheritance, in order to use abstract Core Data entities. –  Oct 07 '15 at 20:36
  • You seem to understand how to prove properties of these relationships in a way I don't know yet. I'm interested to know if the FinalSelf in "FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf" (which compiles in C#) I added above can mean anything than "the struct or class that I am or a class derived from me". After such a thing is defined, you can use it where a "Protocol"is needed, but as for that first definition, I don't know, but it seems to mean this alternate concept of Self that I'm talking about. –  Oct 09 '15 at 16:48
2

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.

Kametrixom
  • 14,673
  • 7
  • 45
  • 62