I'll post my thoughts on the pitch (of which I'm very much in favor) in another post. For now, I wanted to add some practical usage data to the discussion.
I took the latest nightly toolchain and used it to port the Mac target of NetNewsWire to implicit
some. It's a fairly large-sized real world project. I only updated the Mac target, and only the app project itself, not the packages on which it depends, which stayed with the current behavior.
The experience went pretty smoothly (I filed some diagnostic improvements that would have helped along the way) and took me about half an hour. Every change was mechanical – I basically added
any wherever I needed to to make it compile. In all cases this mechanical translation was "correct" – there were a few places where I could have fiddled with the code to instead make it generic, but didn't.
After migration, the code compiled and worked both with
-enable-experimental-feature ImplicitSome and without it.
Overall there were 319 needed additions of
any. Note that these are the requirement of of SE-0335 rather than this pitch. Incidentally, this has solidified in my mind that the parenthesis in
(any P)? have got to go. This was discussed during the SE-0335 but needs revisiting based on (my :) experience in the wild.
By far the biggest cause was
Result<_, any Error>, of which there were 205 occurrences, almost entirely found in callbacks. Presumably these will all go away as codebases migrate to
async functions instead.
The remaining 200 or so
any were mostly either:
- Storage: delegate protocols where the
anyis there to represent it could be any delegate (you could model this instead by making the type generic over its delegate, but that is probably not worth doing), or heterogenous collections. In these cases the
anyadds useful meaning. It really could be any of a number of type-erased types.
- Optional Arguments: the function was being passed an existential, and it was optional, so the concrete type may not be available when opening the existential. In some cases, some local refactoring could have eliminated the existential, improving the code slightly. In others places, use of
anywas the best option.
- Conformances to protocols that took
any ObjCTypeand that cannot be generic because of the requirements of
@objc. It is worth discussing a targeted carve-out to default imported ObjC protocols to be
Offsetting these, I counted about 40 or so places where
some was in the code but could be eliminated – all uses of it in SwiftUI (
some Widget etc). There is currently a bug in the nightly toolchain so I didn't actually do this migration.
NetNewsWire started as a *Kit app so has relatively little SwiftUI. This will presumably increase significantly over time. An app that was all SwiftUI would likely eliminate several hundred of these (it might be interesting to try porting Ice Cubes or MovieSwiftUI). Subjectively, my view is that these
some View annotations are pure noise, adding little readability benefit. This is in stark contrast to e.g.
[any View] storage where the heterogenous nature of the storage is of significance. I'll say more about that when I post my pitch response.
After I did this port, I then went back and switched to
-enable-upcoming-feature ExistentialAny instead. This required me to go through and add 60 additional annotations. These were mainly either on arguments where the function was now generic but functionally equivalent to an existential, or they were on
is where the annotation doesn't really have much effect. Again my personal opinion is these extra annotations were just noise without readability benefit (will post further on this), and added additional migration effort without much benefit.