Your Dog.greet here is not a valid implementation of the protocol requirement on Animal - you require that any type that implements Animal.greet be generic over any other Animal implementation, and then in Dog provide a totally separate function that is non-generic, but happens to share the same name. With the default implementation provided, the compiler will always select it in a generic context like in walk (since the other greet literally doesn't fulfill the protocol requirements).
I don't believe there's a way to accomplish what you're trying to do with a generic protocol requirement without inspecting the type at runtime - something like:
struct Dog: Animal {
func greet<A: Animal>(_ animal: A) {
if type(of: animal) == Dog.self {
print("Bark in excitement")
}
else {
print("Dog sniffs \(A.self) cautiously")
}
}
}
Your Dog.greet here is not a valid implementation of the protocol requirement on Animal
Indeed it is not. However, since I provided a default implementation of Animal, I was hoping the compiler would have some heuristic to realize the Dog's function was just a specialization over the generic one. It does make sense that the compiler see those functions as completely different, though.
I don't believe there's a way to accomplish what you're trying to do with a generic protocol requirement without inspecting the type at runtime.
This is what I was fearing. The example I wrote is very basic for the purpose of discussing here in the forum, but in my code the Cat and Dog structures are simple generic or variadic types, making it impossible to test at runtime. At least I don't know to runtime test a variadic type to provide a concrete greet(_:) implementation.
This would only be possible with a generic system that’s completely compiled away, like C++ templates. Swift, like many other modern languages, provides runtime support for generics, which means it would have to emit code like what @MPLewis wrote anyways for this to work.
You probably want to apply double dispatch pattern here: Double dispatch - Wikipedia. It’s a standard technique in all OO languages and not really specific to Swift or protocols.
You want two protocol requirements, where the first one calls the second requirement on its argument, receiving self as a parameter:
Just wanted to comeback and thank you again @Slava_Pestov. I've been implementing a solution in your suggested direction and it is currently working fairly well (aside from all the indirection). Thanks