Occasionally, we have scenarios where we want to do this:
protocol Protocol { ... }
protocol SubProtocol: Protocol { ... }
func f<T: Protocol>(x: T) {
...
}
func f<T: SubProtocol>(x: T) {
// do something different when T is a more specific type
...
}
But this isn't always possible, for example, if f<T: Protocol>(x:)
is a protocol requirement, there won't be any dynamic dispatch for this, so the second f
will never get called.
We can instead try to write the above like this:
// unrelated function that needs to exist for this example
func g<T: SubProtocol>(x: T) { ... }
func f<T: Protocol>(x: T) {
// if SubProtocol has Self or associated type requirements, this won't even work
if let specialX = x as? SubProtocol {
// specialX: SubProtocol
g(x: specialX) // error: cannot invoke 'g' with an argument list of type '(x: SubProtocol)'
}
}
Now, generics are determined at compile-time, so there isn't really a need for this whole dynamic casting business. I propose the following:
func f<T: Protocol>(x: T) {
#if <T: SubProtocol>
// x: T, but also T: SubProtocol
g(x: x) // success
#endif
g(x: x) // error: argument type 'T' does not conform to expected type 'SubProtocol'
}
I don't entirely know how generics are implemented in Swift, but if they're fully (or even partially) specialised at compile-time, this check can simply be evaluated for each specialisation and generate different code as needed, which doesn't sound extremely hard (correct me if I'm wrong...).
I used #if
syntax because this is a compile-time thing (edit: I was wrong, working out more appropriate syntax in the replies); suggestions for improvements are very welcome.
What do people think of this? Does it sound like a useful/appropriate feature?
(Inspired in part by this discussion, and my StackOverflow question)