Issue #188 01 Jul 2021
Written by: Kristaps Grinbergs
It seems that WWDC21 has settled down, and people are starting to enjoy the summer. But Swift’s core team hasn’t stopped working, and there are quite a few proposals in review as well as accepted.
This issue of Swift Weekly brings some sad news because the Swift Unwrapped podcast has ended. I want to personally say thanks to Jesse Squires and JP Simard for running Swift Unwrapped for so long - 4.5 years and 92 episodes. It was a wild ride, and I learned a ton. Thank you!
Swift Weekly is looking for sponsors. If your company or someone you know would like to sponsor this community-driven project, reach out! Thank you!
Interested in sponsoring Swift Weekly Brief? Learn more here.
- SR-14824 [Compiler] Improve diagnostic for multi-statement closures instead of saying “too complex closure return type”
News and community
The third review of SE-0311: Task-local values concluded a few weeks ago. The review focused on a narrow question: whether to extend the proposal to allow task-local values to be established from synchronous functions. Feedback on this point was quite positive. SE-0311 is therefore accepted without further amendment.
It was generally accepted that overloading on
asyncwould be beneficial. Most of the discussion centred not on
async, but on
throws. Accepting this amendment makes
asyncmore different from
throws, and there was some sentiment that this amendment should only be accepted if we also allow overloading on
throwsaccordingly. The Core Team disagreed with this approach for two specific reasons:
throwsis not necessarily as amenable to overloading as
async, due to the presence of control flow that handles thrown errors without requiring the enclosing context to be
catchconstructs have no analogy in
async). It is not a given that
asyncmust be consistent with respect to their overloading behaviour.
- Second, overloading on
asyncis timely in a way that
throwsis not. There are no
asyncAPIs now, but the Swift community will be adding them as soon as Swift 5.5 becomes available. Without overloading on
async, the Swift ecosystem will end up with a nontrivial number of APIs with non-ideal names (e.g., an
Asyncsuffix) that won’t be able to be fixed later.
throwsis different, because the overloading rule has been in place since Swift 2.0. Delaying the ability to overload on
asynchas long-term costs that the Core Team feels are not justified by the potential for inconsistency.
The second review 4 for SE-0316: Global Actors 7 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
@globalActorimpart global-actor-ness on their subclasses, and @filip-sakel raised questions about the ramifications of global actor subclassing on the type system 4. 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
GlobalActorto be used as global actor attributes, as in
@T func foo<T: GlobalActor>(...). @Zhu_Shengqi noted that this could create readability problems 2, since it isn’t clear what
Tis 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
@globalActoron subclassable classes, there would also be interesting interactions possible where a class may inherit a
GlobalActorprotocol conformance, but not be marked
@globalActoritself, 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.
Proposals in review
Based on the discussion of the first pitch, this ability was removed from the proposal. However, experience augmenting existing Swift libraries with async/await functionality has demonstrated that this overloading might be useful, so we would like to reconsider the limitation.
Swift’s type inference system is quite powerful, but there are many situations where it is impossible (or simply infeasible) for the compiler to work out the type of an expression, or where the user needs to override the default types worked out by the compiler. Directly referencing the heavily-overloaded
Double.initinitializer, as seen above, is one such situation where the compiler does not have the necessary context to determine the type of the expression without additional context.
The previous review ended on June 7th. Relative to the previous review, the following changes have been made:
- Added the
GlobalActorprotocol, to which all global actors implictly conform.
- Remove the requirement that all global and static variables be annotated with a global actor.
- Added a grammar for closure attributes.
- Clarified the interaction between the main actor and the main thread. Make the main actor a little less “special” in the initial presentation.
The first review 7 received a lot of very useful feedback. In response, the authors have made several changes to the proposal for the second review, summarized here:
YieldResultto express the action of yielding’s impact, either something is enqueued, dropped or the continuation is already terminated
init(unfolding: @escaping () async -> Element?)to offer an initializer for unfolding to handle back-pressure based APIs.
AsyncThrowingStreamgeneric on Failure but the initializers only afford for creation
where Failure == Error
- removed the example of
DispatchSourcesignals since the other
DispatchSourcetypes might be actively harmful to use in any async context
- initialization now takes a buffering policy to both restrict the buffer size as well as configure how elements are dropped
I wrote a function
async(timeoutAfter:work:). Its goal is to run an async task with a timeout. If the timeout expires and the work hasn’t completed, it should cancel the task and throw a
Property Wrappers are responsible for wrapping common getting and setting boilerplate and also for storing any auxiliary helper properties. Often, these helpers are constant across different instances of the wrapper, not changing after initialization. Thus, having to store these properties in each individual wrapper instance should be avoided. In the following
Clampedexample, every wrapped instance will store its own
range— even though there isn’t a way for this range to change across different
.. requires any
nonisolateddeclaration to only involve
Sendabletypes. This eliminates the potential for data races due to non-
Sendablevalues being accessible from any actor.
Over the last few years, the
importstatement has been collecting unofficial, unsupported features to help manage the dependencies between libraries. We (@xymus and @beccadax) are thinking about how to stabilize some of these into officially-supported language features.
Chief among them is the
@_implementationOnly importis completely hidden from clients who import your module. This allows clients to import your module even if they do not have access to that module, so it’s great for hiding libraries that you use only as an implementation detail. To make this work, though, the compiler stops you from using a declaration imported via an
@_implementationOnly importin a
@usableFromInlinedeclaration (including the function body if it’s
@inlinable) if that use would be visible to your clients.
With the acceptance of SE-0215,
Neverwas deemed as being a “blessed bottom type”, but that it wouldn’t implicitly conform to all protocols—instead explicit conformance should be added where valuable.
The conformance of
Hashablein SE-0215 was motivated by examples like using
Neveras a generic constraint in types like
Resultand in enumerations. These same use cases motivate the conformance of
Identifiable, which is pervasive in commonly used frameworks like SwiftUI.