Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to me as the review manager by DM. When contacting the review manager directly, please put "SE-0466" in the subject line.
What goes into a review?
The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:
What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at:
Copied from the pitch thread, since it didn't receive any response there, and doesn't seem to have changed a great deal since the pitch:
I still don't believe this is necessary; that is:
it only saves you from writing @MainActor; there's no dramatic reduction in complexity here.
it's basically only relevant on Apple platforms
the number of @MainActor that this can realistically elide is small, since most declarations in small app codebases for Apple platforms are already implicitly@MainActor (eg. {UI,NS}View{Controller,} subclasses, View types).
Essentially, the only place where it's going to help are
ViewModel-type classes, where anecdotally I think most projects have a superclass or protocol for VMs to conform to which could add an implicit @MainActor already, and by and large it doesn't make a difference since they're rarely calling @MainActor APIs anyway, but sure. Maybe there's something to be said here.
global variables, which… I think "making global variables usable" should be an anti-goal of anything. That doesn't help anyone. Just remove mutable globals from the language entirely.
And the cost of it is, that you can never know from any Swift code snippet, whether that code is correct (because it can depend on the dialect), and copy-pasting an answer to a forum post into your app might not work (in either dialectal direction). Every Swift Q&A ever asked will probably have to state which dialect they assume.
A couple thoughts for "ways it could be less divisive":
make the implicit global actor only apply to class types and global variables: most other types of declaration, it doesn't matter, and these two types of declaration specifically are what's hard to concurr-icize. Most value types are already fine, actors are already fine, most free functions are already fine.
infer the implicit global actor only for non-public declarations: same rule as Sendable; make the author explicitly declare intent if they're making something other people can depend upon.
All that said though, if this is considered necessary (and it seems like that ship has sailed, sadly), I think this is pitched the right way around — an easy opt-in for people who want it (possibly including "new Xcode projects"), whilst most OSS (swift package init, etc) get the more general language by default.
Overall I think this proposal is fine; It's a bit unfortunate to have language dialects but on the other hand, it's more like tweaking some defaults of things which is rather understandable and e.g. scripts not having to care about most of this etc.
As I understand this proposal this is going to be opt-in, and I'd encourage the implementation to just leave it off for most swift packages (swift init created packages), so there should not be much impact on package developers. I can see the utility for small toy apps and maybe app developers.
This is going to be huge for enabling adoption of Swift Concurrency and Swift 6 mode in large apps. At Airbnb our approach for adopting concurrency in the feature layer is more or less "make everything @MainActor", but this is tedious and noisy. Allowing us to make this the default will meaningfully simplify adoption here by reducing the amount of cognitive load, ceremony, and boilerplate.
I have a feeling that we would enable this for almost our entire app. I personally think this is a great default for application code.
It's probably a great default for legacy OOP code, but from my experience building new SwiftUI apps (and some libraries) from scratch in Swift 6 mode, the impression is that they wouldn't need it.
So in effect, this proposal encourages sidestepping structured concurrency altogether, because I presume to some (many?) developers it would easier to just snap the compiler flag and move on. I'd even expect a lot of low quality StackOverflow answers that suggest using this flag.
So then what happened to the idea of using as many CPU cores as possible, in a safe manner?
I work on several courses that teach Swift from scratch, and a feature like this is absolutely required. Swift 6 is unteachable at the moment if one wants to maintain progressive disclosure.
For my own courses, I'm sticking to Swift 5 language mode until this feature is released. In other courses that I work on, authors have chosen to adopt Swift 6, which forced them to add @MainActor in places they really don't want to (as the reader is nowhere close to learning about concurrency yet).
Authors that use playgrounds have it even worse, as those just plain crash on code that is valid according to the compiler, but still crashes LLDB.
Unfortunately, I won't have time to actually try this out during the review window, but this proposal does seem to fix the issue at hand. However, it does still violate progressive disclosure somewhat as it uses words like "isolation" and "actor". That's not ideal for my specific use case, but it's definitely acceptable, and I'm happy to see it proposed.
I'm curious, could you show some examples of code you use for teaching that don't work in Swift 6?
I'm thinking, maybe it's the examples themselves that need to be changed, i.e. maybe teaching basic concepts before explaining concurrency with Swift 6 requires some new approaches.
@crontab Pretty much anything involving global / static variables. This was well explained in the vision document and pitch.
Swift's progressive disclosure is great for teaching beginners, as you can start from scratch and just add one topic at a time. That means early examples are just a single main.swift file with some global variables and functions. These then get split into separate "themed" files, each containing a coherent subset of variables and functions, which then eventually evolves into custom types.
So while I understand the concerns regarding enabling this for "real" apps, the problem is getting beginners to that stage where they can write proper, concurrency-safe apps.
The proposed solution of leaving the default as is and requiring an additional setting in Package.swift works fine for me. It's just a small addition that is easy to explain to beginners, and it keeps them mindful that what they're doing is fine for now.
But global variables imply top-level code, right? So what if students are taught "top-level code is bad unless you are experienced enough to know what you are doing"?
This has been discussed before in various posts, global variables are a bad idea one way or another.
And I think the elephant in the room is that:
(1) Swift is not a scripting language (anymore) despite that it kind of allows scripting-style programming
(2) Swift is not an OOP-first language (anymore) with its recent drift towards functional programming with SwiftUI and structured concurrency, neither of which are OOP-friendly.
So my question is: why would you teach outdated concepts that your students are not going to use in the future anyway?
Don't get me wrong, I put any top level code I write into a do block just to avoid top level variable weirdness, but the entire point of having top level code in the first place was to make teaching easier. Having to teach students not to use it as the first lesson seems like a step backwards.
OK but at least the Xcode Playground seems to be Ok with top level code and variables? I.e. anyone can experiment or learn the basic concepts in the playground, but the moment they try to build an actual app, be it CLI or GUI, I think they should also understand that they are stepping into the territory of concurrency and generally a place where global stuff is not welcome.
I agree that this is not fundamentally a dramatic reduction in complexity. The goals of this change are:
Improve progressive disclosure by changing when you need to confront the concept of actor isolation in the Swift 6 language mode.
Reduce the number of false-positive data-race safety diagnostics that you get when writing single threaded code in the Swift 6 language mode.
It's certainly true that Apple's platform SDKs contain a significant API surface that is isolated to the main actor, but I don't agree that a main actor mode is only useful on Apple platforms. I believe that a main actor mode is useful for anybody writing a single-threaded executable program, especially beginner and novice programmers. But I also acknowledge that most of the language features that will have fewer false positive data race safety errors under a main actor mode -- global variables, classes and class inheritance patterns, etc -- are features that many programmers (usually more experienced ones) prefer not to use, so I can understand why someone might not think this change is necessary.
I believe that mutable global variables are fine if they are used safely. You have made it very clear that you do not agree with this, and I don't think we're going to agree. That's okay. In any case, I'll elaborate on my views:
Today the Swift 6 language mode makes using global variables unnecessarily difficult, and forces programmers to learn about actor isolation just to write single threaded code that uses a global variable. This is harmful to progressive disclosure. I've seen many arguments that claim that beginners should not learn about global variables, but the reality is that many introduction to programming courses use global variables; it's useful for beginners to have a starting point where a variable is accessible from anywhere, so they can learn about scoping rules, function parameters and results, etc. later.
I believe that removing mutable global variables from the language entirely is a nonstarter. Beginners aside, there are a lot of programmers who make use of mutable global variables. Even if I believed that global variables were a code smell, removing the feature from the language is not a good approach to encouraging programmers to move away from global variables and toward patterns that leverage local reasoning. I believe this is the kind of mentality that has made many programmers believe they are "not smart enough" to use complete concurrency checking. Telling programmers they're "holding it wrong" for patterns that use mutable state only on the main actor, where it's perfectly safe, is hostile. The programmer isn't doing anything wrong by using a global variable only on the main actor. And if we require them to write @MainActor explicitly on the global variable and every transitive use site of it, then we've made something that was previously very simple to express much more difficult for no apparent benefit in a codebase that makes little-to-no use of concurrency.
I don't think this approach is sufficient because the moment you need to use a global variable, you need to transitively propagate @MainActor annotations throughout your project. I don't think this is a better model for progressive disclosure - you still need to confront actor isolation when you are not trying to write concurrent code.
A lot of people use libraries as a code organization tool, and separate libraries aren't necessarily meant to be consumed outside of the project. If you have a library that's part of some executable project and not used elsewhere, it's reasonable to want that code to also default to main actor isolation. Maybe there's a package-related rule that we could use instead here. I agree with you that it's better for public API in a library that's meant to be consumed by external clients to be explicit about what the actor isolation is. However, I don't see how a rule like "this type is main actor isolated within the package but not outside of it" (which is what the implicit Sendable rule ideally should be for public types if we had a notion of package access level on protocol conformances) could work for main actor isolation.
Yes. If everything in a program defaults to the main actor, then diagnosing access to state in a deinit is always a false positive. I personally think it's worth re-considering the general rule in SE-0371 that requires isolated to be explicit on a deinit of a global actor isolated class.
The risks of language dialect leans into an IDE/compiler, but doesn't really consider code reviews, which is just plain text.
I've mentioned elsewhere, iPhone/Mac/Apple app developers make up the vast vast vast majority of Swift users today and probably 10 years from now too, but this caters to the minority case.
I guess I just really disagree with the justifications in this doc, but it seems already decided :/.
Would it be enough to only enable this behaviour in a file with top-level code, along with any type decorated with @main? When a student (or other kind of learner) is adding a second file to their project, is it still too early to discuss isolation?
+1 to this. At GetYourGuide we do have a large-scale modular app, and I'd definitely enable it for all feature, UI and presentation modules.
Adopting Swift 6 concurrency across the app has been a long process and such flag would be quite handy, I love it.