In another thread, a conversation came up about global actors. At issue was that, today, the compiler cannot assume that an instance of a global actor type actually corresponds to the shared instance itself.
I'm just going to reproduce the motivating problem from that post here:
@MainActor
func mainIsolatedFunc() {}
func execute(isolation: isolated MainActor) {
mainIsolatedFunc() // Error: Call to main actor-isolated global function 'mainIsolatedFunc()' in a synchronous actor-isolated context
}
A similar problem also manifests itself within the standard library with MainActor.run. This is not an extension on GlobalActor, because today such an extension cannot be written.
It seems to me like really this boils down to Swift's inability to guarantee the contract of a singleton type. Independent of how you feel about the singleton pattern, it is undeniably in very wide use. It is used extensively in Apple's SDKs and with global actors, is a core part of the concurrency system.
If we imagine:
a) it was possible to guarantee that all instances of a type corresponded to a single, shared instance
b) global actors used this facility
would it help?
I can see it would address the example above. And I think it would also help with a GlobalActor.run implementation.
extension GlobalActor {
func run<T>(
resultType: T.Type = T.self,
body: @Sendable (isolated Self.ActorType) throws -> T
) async rethrows -> T where T : Sendable {
// with this facility, it could be statically proven that `body` will always be
// isolated to GlobalActor.shared. Is that enough?
}
}
Would a change like this be enough to sufficiently address the inability to express global actors with generics? I don't feel like I have a good handle on the limits people have run into here.
Another interesting and related issue to global actors specifically is that the @globalActor annotation does not have to be applied to the actor type. This feels like an unusual level of flexibility. What are the use-cases for that?
Again from that thread:
actor A {}
@globalActor
struct SomeGlobalActor {
static let shared = A()
}
This is all just exploratory. If you can come up with holes and/or mistakes I'd really appreciate it. Thank you!