Your vision document identifies a bunch of pain points in swift 6.
I think you're absolutely right to focus on these - but I would argue for a radically different approach.
Swift 6 is currently trying to do a technically very hard thing in analysing code, and making data safety guaranteed.
Unfortunately, this has resulted in side effects:
- Significant language complexity
- There are a large number of annotations
- Almost everyone who actually uses Swift doesn't understand the subtleties of how the compiler reasoning and compiler annotations actually work
- Practical pain interfacing with existing/framework code
- protocol conformance issues
- incompatibility with basic language features like codable
- etc
- 'Actor infection'
- once you mark something as @MainActor, it's hard to stop that proliferating around code that interacts with it
- Complexity
- The solutions to 'make the compiler happy' often add ugly complexity (e.g. creating sendable wrapper to pass an NSImage)
- Frustration
- sometimes from not understanding the language features, sometimes from not wanting to jump through the hoops that Swift 6 requires
I'm sure there are more - @hborla does a good job of expanding on many of the details in the pitch.
My reaction to these has been simple. I'm just not using Swift 6 mode. And I'm still plagued by annoying warnings in my swift 5 code.
Having identified the problems, the vision proposes making it easier to move to single threaded programming in large areas (potentially even as a default for many programmers)
I think this is entirely the wrong approach. It is led by what the compiler needs rather than what developers need.
I would propose a very different approach
1) Proper documentation
The first step is to actually document what swift 6 does.
This should be in the swift language guide.
For example - 'sending' solved a problem for me yesterday in swift 6 mode, but searching the Swift Language Guide finds no references to the term. *
I suspect simply the process of documenting it will result in significant insights to areas that can be improved.
The current state of play is that the only way to understand the language is to keep up with a whole series of proposals, and how they build on each other. The folks writing the language naturally do this, so perhaps they assume that regular Swift 6 users do too.
This is wrong - I have been involved in several threads on this forum where the most engaged swift 6 users still have to be corrected about how the language works ('doesn't SE-xxx do yyy...').
Once complete documentation exists, future changes should not be accepted unless they fully explain themselves fully in the documentation.
The complexity of writing user documentation will highlight a lot of the practical complexity.
2) Opt In to concurrency safety
Developers should be given sharp knives
Flip the default. The default should be that guaranteed data race safety is turned off.
If I want to return an NSImage from a method, and consume it in another thread - that's my business.
If I want to opt in to guaranteed thread safety for my module/class/app, then you can give me a compiler error.
The reality of my coding is that data race errors are a vanishingly small part of the bugs I have had to fix over the years. It would be nice for the compiler to help me avoid some of those bugs - but only if the pain is proportional to the benefit. Right now, it is not.
I have a handful of production apps that do significant processing of images. I have never had a data race issue where one thread modifies an NSImage while another thread is trying to use it.
But I have still wasted a bunch of time trying to get rid of warnings about returning an NSImage from a method. That added pain for zero gain.
Moving to an opt-in model will change the dynamic. At the moment, it feels like we're on a forced march to the promised land of Swift 6 safety. If safety is opt in, then developers will choose to use it as it becomes more ergonomic. If the feature has to be worth the pain to convince people to opt in, - the dynamic around design will focus more on real usage.
3) Concurrency tooling
Analyse code was a great tool as we moved towards arc (and even later). Run the tool, examine warnings about memory safety, fix if needed.
It took a very different approach though - the compiler didn't start refusing to compile my old Obj-C code. It gave me a way to gain insights to improve my code.
Concurrency could do the same thing. Analyse could warn me that returning an NSImage is potentially unsafe if the sender keeps and mutates the original - but I can choose to ignore that because I know I'm not doing so.
4) Update the framework/language to better 'play nicely' with concurrency/isolation
e.g. Extend codable so that it can work on a @MainActor struct/class
I know this is going radically against the grain of the current vision. The new vision document recognises much of the pain inherent in the current approach. Apple probably has (or could have) data on how much code is opting in to swift 6. My guess is that number will show the observable choice that developers are making.
The goal is to
make basic use of concurrency simple and easy.
When your solution to a problem is to throw out concurrency entirely for large sections of language use, then you're missing a bigger problem.
- A similar documentation problem exists for @MainActor annotations. The term is critical to current swift code, but barely referenced in the Language guide. As a result, massive errors in understanding what it does, and doesn't guarantee have been common - even in Apple WWDC presentations.