I think there's a fundamental misconception here which needs to be cleared up.
If you override an open member of a class, or if you implement a protocol requirement in a conforming type, then that acts as a "specialized" version of the overridden member or default implementation (respectively). In the general case, the choice of implementation used is determined at run time based on the dynamic type of the instance on which the method is invoked. There is a performance and memory cost to dynamic dispatch. If the compiler can work out for sure which implementation will be called, then as an optimization, the choice can be baked in at compile time.
It so happens that the specialized implementation must use the same name as the overridden/default implementation. There is no reason that this must be the case forever in Swift, and in the case of protocols, there's in fact the not-yet-official @_implements
that allows one not to use the same name.
Which brings us to the central point I think needs to be cleared up here: There is nothing magical about naming two functions identically with different generic constraints; no table exists at run time to choose between them. All you have are two functions with the same name, thus potentially requiring the use of complicated overload resolution rules to determine at compile time which one the user intended to invoke. One function is a "specialized" version of the other only in the sense that when a user types the same name, at compile time there are these overload resolution rules to decide which one the user means based on context such as type annotations.
In this case, you have two free functions named getSequence
. Fortunately, when you invoke getSequence()
, it's unambiguous which one you mean. It's unambiguous because you can only choose a method inside create
that can return any SequenceParent<T>
where T
is of the caller's choosing.
To clarify what the compiler is doing, it would be simpler to name the first getSequence
instead as getSequenceA
and the second one as getSequenceB
. (Again, there is no magic you unlock by using the same name for these two functions.) Then, you will easily see that inside create()
you invoke getSequenceA
. Therefore, when you iterate over the returned value of type ElementSequence<[Int]>
, which next()
is called becomes obvious.