SE-0335 has been accepted and on it’s current trajectory will eventually impose a source breaking change to the Swift language where any methods that have argument types which are a protocol will need to be rewritten to prefix the protocol name with a new keyword any
. The review breezed through and this is on all our horizons and already available in Xcode 13.3.
The specifics of the change are rather long winded and revolves around the overhead associated with the use of “existential containers” which some feel should be surfaced in the language and made explicit.
To cut a long story short, there are two nearly equivalent ways to pass something conforming to a protocol to a function as discussed in these great, articles
// First currently "existential" form
func startTraveling(with transportation: Drivable) { }
// Second "generic" form
func startTraveling<D: Drivable>(with transportation: D) { }
The second form has a couple of advantages. To begin with the second form runs faster (though I feel these concerns may have been overblown) but more importantly it is more flexible and you don’t encounter the dreaded protocol can only be used as a generic constraint because it has Self or associated type requirements
error which is trying to tell you to change the function signature to the second form if your protocol has these requirements.
The approach taken by SE-0335 seems to be to introduce friction to the first form so people are aware they are producing slower and more limited code in the hope that they move to the second far less intuitive form. This seems a very inefficient use of a lot of peoples time.
I’d like to propose a different approach:
As the second form is effectively an equivalent and better form of the first, I feel the compiler should automatically, internally rewrite functions in the first form into the second form from the point of view of the rest of the program and libraries importing the module containing that code. The source file would retain the succinct, first from. If you look at it this way the necessity to even discuss existential containers disappears. In a sense this is the way the compiler should have implemented passing protocols in the first place given the relationship between constrained generics and protocols in the implementation of Swift. View the first half of this video if you want to understand what I mean. Generic functions are not always specialised and faster as a result but typically use dispatch through “protocol witness tables” as does the first form of function discussed above. This is another thing developers just don’t need to know about.
If this approach were taken Swift 6 would not include a source breaking change but an internal code rewriting optimisation that makes less optimal code run faster. Is there a reason I'm missing why this has not been considered or is this the long term plan and why couldn't it be introduced now? You could still have the option of using the any
keyword if you needed to go existential.