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
any
is 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 theany
adds 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
any
was the best option. - Conformances to protocols that took
any ObjCType
and that cannot be generic because of the requirements of@objc
. It is worth discussing a targeted carve-out to default imported ObjC protocols to beany
.
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 View
, 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 as?
or 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.