You make a compelling point here. That said, the fact that I’m only now learning about this behavior after using Swift for ten years suggests that typealias might not be the most intuitive mechanism for this kind of functionality — even in its current uses.
It’s particularly surprising how a plain, top-level typealias — without any special annotation, syntax highlighting, or even autocomplete support — can influence program behavior in places where it’s not explicitly referenced. It almost feels like this was intentionally made obscure, perhaps to discourage widespread use. While that might be reasonable for something like integer literal default type control, it seems less appropriate for something as prevalent in modern Swift as isolation control.
I'm definitely sympathetic to this, and I think what's weird about the typealias form is that what you are specifying with private typealias DefaultIsolation = MainActor or private typealias DefaultIsolation = nonisolated is not a type. Though isolation can be represented with a type in the case of global actors, isolation in general is not a type, and what you're configuring with this typealias is a declaration attribute/modifier.
I agree that it's still worth pursuing the #SwiftSetting proposal for controlling compiler diagnostics, including enabling strict memory safety diagnostics, configuring strict concurrency warning levels in language modes < Swift 6, warning levels, and any other diagnostic controls we add in the future, and perhaps the spelling of the macro should be clear that it's specific to diagnostics given the limitations on supporting other compiler flags (as discussed in the pitch thread).
The reason why I proposed this feature for controlling default isolation per-file separately is because I am not at all convinced that a general per-file compiler settings feature is appropriate for things that significantly change language semantics like default isolation does. Default isolation is very different from the other compiler flags that you listed because it is a language dialect, while the other flags just configure diagnostics - the code you write means the same thing regardless of which diagnostic control flags you set.
That said, I'm definitely open to other solutions; I do acknowledge that using a typealias for this might also not be the best approach because what you're specifying isn't a type. I went with the typealias approach because of precedent with other similar defaulting rules, but maybe it complicates the mental model by encouraging you to think about isolation as a type.
Off topic
This is not really the purpose of this review thread, but this is not true for the #SwiftSetting, and I do not believe this is true for any language proposal. The specific comment that you linked to is actually pointing out a real challenge with upcoming features changing type checker behavior and how to handle that in swiftinterface files. That's one of many issues pointed out in the pitch thread.
Under the current proposal, this works if you have set DefaultIsolation in the file. It does not work if you only have -default-isolation set for the module or you're using the default, because the DefaultIsolation typealias is not declared anywhere.
Yeah, I think main.swift and playground files should have their default isolation set to MainActor. Top-level code currently uses a sort of hybrid of this where global variables are inferred to be @MainActor but nothing else is, and that causes issues when you try to access those variables from unannotated methods and types in that same file.
I completely agree with this, and I'm pretty concerned that if we lump this into a general #SwiftSettings feature, the default isolation could easily get lost in a list of unrelated compiler flags (you might have many of them for controlling the behavior of various warning groups). I also think it'd be strange for diagnostics that point out the source of actor isolation inference to point at a specific argument to the #SwiftSettings macro.
No matter which direction we go in, the diagnostics that point out the source of actor isolation inference when you make a mistake will show you exactly where this is declared when you need to know about it.
This thread saw a bunch of activity in the first few days, but has since gone quiet; as review manager I'm bumping it in the hope that anyone who missed those first few days sees it and has a chance to comment before the review period ends.
I'll admit that I find the typealias to be an awkward way to write this, especially because of how weird nonisolated ends up being. I would personally prefer something like
using @MainActor
or
using nonisolated
I don't know if we'll someday find ourselves wanting other file-wide settings like this one, but if we do, it's not unlikely that they'll fit into this same basic design of using <some attribute or modifier>. Anything that can be thought of as a file-wide default will presumably have an attribute or modifier to specify or override that on a finer-grained basis. If @collapsible makes a specific declaration collapsible, it makes sense that using @collapsible at global scope would do that by default within the file, and that's basically what this means.
In the alternative, if awkwardness stems from the proposed typealias being not a bona fide type alias, it seems to me that we could lean in and make it so (though whether advisable to have more churn is another question). Namely:
We could create a bona fide Nonisolated global actor with some magic (and, optionally, soft-deprecate nonisolated for that purpose)
We could fully support DefaultIsolation being aliased to something at all times and have the -default-isolation compiler setting plumbed through to change that value
We could then support, as was asked above, explicitly writing @DefaultIsolation func...
If some of the above is actively desirable, that would reinforce the argument that a typealias is a good spelling for the feature (even if we choose not to complete the implementation right away). If, on the other hand, some of the above is actively undesirable ever, then that would sway me towards @John_McCall's suggestion.
Fundamentally, I don't think there's any way to understand nonisolated as some kind of global actor type that doesn't provide isolation, because the defining characteristic of a global actor type is that it does provide isolation. There's quite a lot of language semantics around that which would not apply to a NonIsolated "actor", such as the fact that all global-actor-isolated function types are sendable. So no, I don't think that's a viable path.
Yeah I don't think "nonisolation" can be expressed as an actor, even if special one. Since actors guarantee the "execute at most one thing on it at once" and nonisolation is explicitly "a lot of things in parallel run nonisolated".
Reconsidering a different spelling does get us out of this weird nonisolated "type but not really" kerfuffle though which is nice.
using might be nice but I'm worried it might conflict with any local "resource manager" thing that we might want to call "using" (using resource being the "run a cleanup afterwards", but maybe we'd call those with resource then...). One could lean into a top level default ActorIsolation MainActor/nonisolated perhaps if we're looking for other keywords to consider? Where <ActorIsolation> is one specific example of thing where a default can be set. We're acknowladging this is special syntax then and putting the keyword on the right would be ok?
If we did stick to the approach of using the private typealias DefaultIsolation = ... syntax, then I think this little suggestion againstnonisolated on its right-hand side should get some more attention:
I also think the above would be a reasonable way to opt out of default isolation in a file.
Playing a lot with distributed actors where Swift already have typealises to define/scope system—would say you’re pretty quick used to it. Adding new keyword will work the same, but you just need to learn it on top of many others, no? In the end it’s quite a specific feature, not sure how easy to discover it should be.
Don’t say we shouldn’t think about typealises vs keyword, more like maybe there are other ways around…
My understanding is one doesn’t exclude the other, both would be nice to have.
I see three completely different configuration mechanisms being discussed within this thread:
The ability to set default types, the approach this pitch and default literal types both use.
The ability to provide default attributes to all top level types in the current file, like what @John_McCallproposes with using
The ability to set per-file compiler settings, like @hborladiscusses
The first one is an obscure feature predating the evolution process, rarely encountered in regular user code (as evident by a simple GitHub search). The third one is proposed and the second is non-existent.
I personally think this feature should wait for anything of the above to come through as an complete feature.
I confess, I know very little about this subject. Let's see if I have
got it right. There was a concurrency thing being introduced
that forces us to make everything run in threads. Right?
As a self-appointed representative of the ignorant, I wonder: why
not just have a dedicated folder, labeled "Main Actor", for all files
that belong to the main thread, and then just casually toss all those
files in it, and be done with it?