[Accepted with Modifications] SE-0316: Global Actors

Hi everyone. The second review for SE-0316: Global Actors ran until June 28th, 2021. The core team has decided to accept the proposal with modifications. A few important points were raised during the review and core team discussion, and the proposal will be accepted to address these points:

  • @BigSur asked whether classes annotated with @globalActor impart global-actor-ness on their subclasses, and @filip-sakel raised questions about the ramifications of global actor subclassing on the type system. The uses for subclassing a global actor seem limited, so for simplicity's sake, non-final classes should not be allowed to be annotated as @globalActor. This restriction could be lifted by a future proposal.

  • As reviewed, the proposal allowed for generic arguments that conform to GlobalActor to be used as global actor attributes, as in @T func foo<T: GlobalActor>(...). @Zhu_Shengqi noted that this could create readability problems, since it isn't clear what T is referring to until you read further into the declaration. Supporting this also imposes technical challenges on the implementation of user-defined attributes, and opens potential new design questions. For instance, if we later add another protocol for another kind of user-defined attribute, and a generic argument is constrained by two such protocols, then which kind of attribute is @T? Is there a way to control it? Furthermore, if we did support @globalActor on subclassable classes, there would also be interesting interactions possible where a class may inherit a GlobalActor protocol conformance, but not be marked @globalActor itself, but nonetheless be used indirectly as a global actor constraint by being bound to a generic type argument.

    For these reasons, the ability to use a generic parameter as a global actor constraint should be removed from this proposal. A future proposal could add this ability, but it will need to consider the design questions raised above.

A few other design points were discussed during the review, but the core team does not think the proposal needs to be changed in response:

  • @filip-sakel asks about the possibility of isolating different accessors to different global actors in the future. The example of SwiftUI @State wrappers, which ought to be readable on the @MainActor, but only writable during UI updates on a @PostUpdateActor, is interesting; however, it has ramifications for the property access model to support this, since many value-semantics operations are not modeled as simple gets or sets but as combined "modify" operations, and it isn't clear which actor constraint would "win" in such a situation. It is an interesting idea, but it deserves to be explored independently.
  • @jmjauer questions why nested functions don't currently pick up the global actor constraint from their context by default. This behavior is however consistent with closures, and with how nested functions behave inside methods of instantiated actors, so the core team thinks the behavior is correct.
  • @anandabits asks whether the GlobalActor protocol should enforce a special "constants only" requirement on conformances, to avoid unexpected behavior when a computed property is used to implement the shared requirement, and may end up getting evaluated multiple times. The core team thinks that requiring a constant expression would be too onerous, since it is useful to use dynamic state to set up global constants. The core team considered whether applying another layer of one-time-initialization caching on the outside of a global actor interface, but does not think the added complexity is worth it. For generic global actor types, this would be technically similar to implement stored static properties on generic types, which is the one place in the language today where someone may in practice need to use a computed property to get around the unimplemented feature in the language.

Thanks to everyone who participated in the review!