How to Implement Protocol Methods That Return Covariant Selfs

How do you implement protocol methods that return covariant Selfs?

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

Implementing protocol static method that takes and returns Self (in swift)

Your * implementation has a few subtle problems. Here's the implementation you mean:

static func *(lhs: Float, rhs: ThingBox<T>) -> Self {
return self.init(thing: lhs * rhs.thing)
}

First, you can't use Self as a parameter type. You have to be explicit. Self means "the actual type" and if you could use that for a subclass, this would violate LSP. For example, say I have types Animal and Dog (with the obvious relationships). Say I wrote the function:

class Animal {
func f(_ a: Self) { ... }
}

with the meaning that Animal.f will take Animal, but Dog.f will only take Dog but will not take a Cat. So you would expect the following to be true:

dog.f(otherDog) // this works
dog.f(cat) // this fails

But that's breaks the rules of substitution. What if I write this:

let animal: Animal = Dog()
animal.f(cat)

That should be legal, because Animal.f can take any Animal, but the implementation of Dog.f cannot take a cat. Type mismatch. Boom. So it's not legal. (This restriction does not exist for return types. I'll leave that as an exercise for the reader. Try to create an example like the above for returning Self.)

The second error was just a syntax mistake. It's not Self(), it's self.init(). In a static method, self is the dynamic type (which is what you want), and Swift requires you to call init explicitly when used this way. That's just syntax, not a deep type issue like the other.


There's no way in Swift to inherit the thing you're talking about. If you want to overload, that's fine, but you have to be explicit about the types:

class OtherBox: ThingBox<Int> {
static func *(lhs: Float, rhs: OtherBox) -> Self {
return self.init(thing: lhs * rhs.thing)
}
}

This does exactly what you want, but it has to be added to each child; it won't inherit automatically with covariance. Swift doesn't have a strong covariance system to express it.

That said, when you start intermixing generics, protocols, and subclassing this way, you're going to run into many, many weird corner cases, both due to the math, and due to current Swift limitations. You should ask carefully if your actual code needs this much parameterization. Most cases I encounter of these kinds of questions are over-designed "in case we need it" and just simplifying your types and making things concrete is everything you need for solving the actual program you want to write. It's not that it wouldn't be nice to build incredibly generic algorithms built on higher-kinded types, but Swift just isn't the language for that today (and possibly never; there are a lot of costs to adding those features).

How to define callable attribute with covariant return type on protocol?

What you're attempting is explicitly not possible with Protocol - see the following in PEP 544:



Covariant subtyping of mutable attributes

Rejected because covariant
subtyping of mutable attributes is not safe. Consider this example:

class P(Protocol):
x: float

def f(arg: P) -> None:
arg.x = 0.42

class C:
x: int

c = C()
f(c) # Would typecheck if covariant subtyping
# of mutable attributes were allowed.
c.x >> 1 # But this fails at runtime

It was initially proposed to allow this for practical reasons, but it
was subsequently rejected, since this may mask some hard to spot bugs.


Since your attribute is a mutable member - you cannot have it be covariant with regards to R.

A possible alternative is to replace attribute with a method:

class Passes(Protocol[R]):
@property
def attribute(self) -> Callable[[], R]:
pass

which passes type-checking - but it's an inflexible solution.

If you have need of mutable covariant members, Protocol isn't the way to go.

Covariant 'Self' type cannot be referenced from a stored property initializer

This is pretty similar to Protocol func returning Self, but enough different that it's probably worth answering separately. There are two issues here.

The first issue is why you need final. As in the above question, the problem issue is that you're making a promise the compiler can't prove you'll keep.

Consider the following subclass:

class LocalFireman: Fireman {
let zipcode: String
init(zipcode: String) { self.zipcode = zipcode }
}

What should LocalFireman.shared return? It can't return a Fireman. You said it had to return Self (i.e. LocalFireman). And it can't return a LocalFireman, since it doesn't have a zipcode to init with. So now what?

Swift doesn't let you get into this corner by requiring you to either nail down the specific type of shared (i.e. it will always be Fireman, even if you call it on a subclass), or you need to promise there will be no subclasses, or you need to require all subclasses to implement init:

required init() {}

OK, but you got that far. You marked it final, and it still complains. Now you're hitting a compiler limitation. The whole point of Self is covariance; it's the dynamic type at the point of calling, not the static type in context. It's not just a handy way to say "my type" and this was an choice (though it's something that could change I think). It means "my covariant type" even if you're in a situation where there can't be any subtypes.

All that said, given the private init, I'm confused why you say marking it final is "against my will." If all init are private, this can't be subclassed anyway, so it seems you want a final class, and if so, then just put the class name where it goes. Self is not for that problem (today).

This leaves open the question as to why you can't do this with a required init. Self as a "type of class" and Self as "type of thing conforming to a protocol" are treated as the same thing. So you can't think about this only in the class situation; anything can "inherit" (conform to) a protocol. So Self can potentially refer to a struct. Structs can be any size. So allowing a stored property to be of type Self creates memory layout problems. (I don't think Swift promises that classes will always be implemented as a pointer, either, so this could cause the same problem for classes, at least in principle.)

You can return a value of type Self from a function, but you can't put one in a stored property (this is true both of static and non-static properties), and Swift also doesn't allow them for computed properties (I assume this is just for consistency). So the following is allowed (and in fact useful):

class Fireman {
required init() {}
static func make() -> Self { return Self() }
}

And this is what Self is for.

Swift: Covariant overrides?

I'm not sure it's necessary to do exactly what you ask - i.e. override getAllAnimals rather than overload it. Using generics is a possible solution - see if this works for you:

class Animal { var description: String { return "Animal" } }
class Dog : Animal { override var description: String { return "Dog" } }
class Cat : Animal { override var description: String { return "Cat" } }

class AnimalResidency<T: Animal>{
func getAllAnimals<T>() -> Array<T> {
return []
}
}

class Cattery : AnimalResidency<Cat> {
func getAllAnimals() -> Array<Cat> {
return [Cat()]
}
}

class DogKennel : AnimalResidency<Dog> {
func getAllAnimals() -> Array<Dog> {
return [Dog(), Dog()]
}
}

let c = Cattery()
c.getAllAnimals().first?.description // "Cat"
let d = DogKennel()
d.getAllAnimals().first?.description // "Dog"

My own thinking would not have used two parallel class hierarchies, however, but to try something more like this...

class Animal {
var description: String { return "Animal" }
required init() {}
}
class Dog : Animal {
override var description: String { return "Dog" }
}
class Cat : Animal {
override var description: String { return "Cat" }
}

extension Animal {
class func home() -> [Animal] {
return [self.init()]
}
}

let c = Cat.home().first?.description // "Cat"
let d = Dog.home().first?.description // "Dog"

Swift: How can I make a function with a Subclass return type conform to a protocol, where a Superclass is defined as a return type?

Before you go much further, I'd recommend some background reading on covariance vs contravariance and the Liskov substitution principle.

  • Return types for methods overridden when subclassing are covariant: the subclass override of a method can return a subtype of the superclass method's return type.

  • Generic type parameters are invariant: a specialization can neither narrow nor expand the type requirements.

The relationship between a protocol and a concrete type that adopts it is more like generics than like subclassing, so return types declared in protocols are invariant, too. (It's hard to say exactly why on first read. Possibly something about existential vs constraint-only protocols?)

You can allow covariance in a protocol by specifying associated type requirements, though:

protocol SomeProtocol {
associatedtype ReturnType: SuperclassType
func someFunction(someParameter: SomeType) -> ReturnType
}

class SomeClass : SomeProtocol {
func someFunction(someParameter: SomeType) -> SubclassType { /*...*/ }
}

Now, it's clear that the return type of someFunction in a type adopting SomeProtocol must be either SuperclassType or a subtype thereof.

Accessing optional method in protocol returns scope error


class MyClass: NSObject, MyProtocol { ... }

This says that MyClass implements MyProtocol, which allows, but does not require myFunction. The compiler can clearly see that MyClass does not implement myFunction, so self.myFunction() isn't a thing. It could be a thing, but it's clearly not a thing. The protocol doesn't play into this.

You could "work around" this by saying "I don't mean MyClass; I mean MyProtocol":

func doSomething() {
(self as MyProtocol).myFunction?()
}

But this is kind of silly IMO. self doesn't implement myFunction and this is known at compile time.

If the point is to implement this via subclassing, the correct way to do that is to just implement it as empty in the superclass:

class MyClass: NSObject, MyProtocol {
func myFunction() {}

func doSomething() {
myFunction()
}
}

This is precisely equivalent to "do nothing" while allowing overrides in subclasses.



Related Topics



Leave a reply



Submit