Swift Evolution Monthly: September '22
Conditional Attributes, StaticBigInt, Stable Sorting, Isolated deinit, Work Groups
After multiple months full of really impactful and fast-paced changes to Swift, such as all the improvements to Generics, the introduction of a shiny new Regex engine, and kicking off the next chapter of programming with distributed actors, things have slowed down a bit in the last two months. This is also the main reason why there was no dedicated monthly issue for August (also, I focused on my first app, which is now scheduled for release on October 11th).
If you are an app developer, probably none of the new proposals I'm summarizing down below is going to cause a "Wow!" reaction, at least it didn't do that for me this time. But I tried to still understand and explain the meaning or direction of each proposal without getting into irrelevant details for app developers.
More interesting topics await you in the Pitches section, which I always sort by my guessed likeliness of interest to the community and limit to a dozen pitches, adding a few more this time as this issue is spanning two months. So if you don't have much time but you still want to know what might be coming next, try reading some of the pitches. The community is discussing great ideas constantly!
Accepted Proposals
The following proposals already presented in the past have been accepted:
- SE-0363: Unicode for String Processing
Rationale ✅ | My Summary 📋 - SE-0365: Allow implicit self for weak self, after self is unwrapped
Rationale ✅ | My Summary 📋
On to not yet presented but already accepted proposals!
SE-0367: Conditional compilation for attributes
Links: Proposal Document 📝 | Acceptance Rationale ✅ | Review 🧵
Conditional compilation is a language feature very much needed when supporting multiple SDKs (e.g. #if os(iOS)
) in an app or multiple Swift versions (e.g. #if swift(>=5.6)
) in a framework. The most common use case probably is an #if DEBUG
for code that we only want to run during development, but not in production (such as additional logging or different API targets or tokens).
But currently, if you wanted to conditionally use Swift attributes such as @objc
or the newer @preconcurrency
(for code that was written without Swift Concurrency in mind, see SE-0337), you'd have to duplicate the code for the entire thing the attribute applies to, such as for an entire type like in this example:
#if compiler(>=5.6)
@preconcurrency
protocol P: Sendable {
func f()
func g()
}
#else
protocol P: Sendable {
func f()
func g()
}
#endif
From Swift 5.8 onwards we will be able to shorten that code to just this:
#if hasAttribute(preconcurrency)
@preconcurrency
#endif
protocol P: Sendable {
func f()
func g()
}
Note the new hasAttribute
conditional directive. This not only prevents duplicate code but also more clearly states why we were using a conditional compilation here (because of the attribute!) and saves us the time of having to look up which Swift version the attribute was introduced in.
SE-0368: StaticBigInt
Links: Proposal Document 📝 | Acceptance Rationale ✅ | Review 🧵
Unless you're working on a mathematical app or framework, you probably won't need to know about the new StaticBigInt
type introduced here. But if you do, you might want to read the full proposal (it's short enough) to learn how you can define your own large integer types that support literals that don't fit into [U]Int64
.
Maybe also worth noting that the official swift-numerics library might soon get larger integer types such as [U]Int256
once this was accepted.
SE-0369: Add CustomDebugStringConvertible conformance to AnyKeyPath
Links: Proposal Document 📝 | Acceptance Rationale ✅ | Review 🧵
This proposal aims at improving the print
ing of key paths such as in print(\User.name)
from currently resulting in Swift.KeyPath<User, String>
which is not very useful, to result in something closer to the call like \User.name
.
This small improvement could help in debugging and make unit tests clearer.
SE-0370: Pointer Family Initialization Improvements and Better Buffer Slices
Links: Proposal Document 📝 | Acceptance Rationale ✅ | Review 🧵
This is a low-level programming improvement that is not relevant for most app developers. For those interested in low-level, the title should clarify the topic.
SE-0372: Document Sorting as Stable
Links: Proposal Document 📝 | Acceptance Rationale ✅ | Review 🧵
Imagine you have an array of books in code, something like this:
var books: [Book] = [
.init(author: "Robert Galbraith", title: "The Cuckoo's Calling"),
.init(author: "J. R. R. Tolkien", title: "The Fellowship of the Ring"),
.init(author: "J. R. R. Tolkien", title: "The Two Towers"),
.init(author: "J. R. R. Tolkien", title: "The Return of the King"),
]
And you call books.sort(by: { $0.author < $1.author })
, you would expect that the three Lord of the Rings books from J. R. R. Tolkien would be sorted above the first Cormoran Strike book by J. K. Rowling, right? You're right, of course.
But would you also expect the three Lord of the Rings books to remain in the right order, starting with "The Fellowship of the Ring"? I guess you would. It's how humans would naturally sort. But technically speaking, all three Lord of the Rings books have the same author, and therefore the sorting criteria treats them all like they're on the same level. How elements that are on the same level are treated depends on the sorting algorithm. Algorithms that preserve the order of equal-level elements are called "stable".
In Swift, the sort
function has been implemented as a "stable" algorithm for multiple years now, but it was never documented to be. So in theory, the implementation of future versions could have changed to a non-stable algorithm. Not anymore: This proposal documents the sort
function's behavior to be stable.
Proposals In Review/Revision/Awaiting Decision
You can still provide feedback for these proposals. The current rejection rate is <10%, so they're likely to get accepted. Revisions are more common.
These proposals already presented in the past were returned for revision:
- SE-0364: Warning for Retroactive Conformance of External Types
Rationale 🔁 | My Summary 📋 - SE-0366: Move Operation + “Use After Move” Diagnostic
Rationale 🔁 | My Summary 📋
On to new proposals in review/revision/decision!
SE-0371: Isolated synchronous deinit
Links: Proposal Document 📝 | Review 🧵 | Revision Rationale 🔁
This proposal is another one that probably won't sound like a huge deal to most app developers. Especially because most didn't have the chance to make full use of actor
types yet (including myself). Introduced with SE-306 in Swift 5.5, they help deal with concurrently executed logic that "acts" on the same data in parallel. A good sign that you should probably consider using them more is when you have classes as data models in your app. Chances are that you might be accessing them simultaneously. And migrating them over to actor
types should be possible, given that actors are also reference types like class
es.
In Swift, it's common for types that hold onto other data or dependencies to automatically get rid of these in a deinit
method, rather than requiring a close()
like method that needs to be explicitly called. When initializers were settled in the Swift actor world with SE-0237, the deinitializer's access was limited to Sendable
stored properties only. All value types ( enum
s, struct
s) and all immutable reference types ( class
es) can be easily marked as Sendable
, which means the data is safe to pass across threads. This proposal weakens that limitation by being smart about the details of the state without compromising safety. This allows a deinit
function to additionally access some non- Sendable
state, preventing the need of having to provide a close()
function or do internal reference counting.
While I don't fully understand this proposal's impact even after reading it twice, it sounds a lot like a "rounding things off" kind of proposal to me, which we will all appreciate once we got more used to writing actor
types. Currently, I guess, it mostly affects Server-side Swift developers and lower-level framework authors.
Recently Active Pitches/Discussions
Some threads inside the “Evolution” category with activity within the last 2 months I didn’t link yet. I’ll cover them in detail once (and if) they become proposals:
- Explicit protocol fulfilment with the ‘conformance’ keyword
- Parameter Packs
- One-Element Tuples
- A new keyword that stands in for a tedious initializer of arbitrary length
- Optional explicit `self` parameter declaration in methods
- Allow opaque result types in protocol requirements
- Build-time SDK availability check
- Pointer bit width compile time conditional
- Implicit return in single-line switch cases
- Allow Package.swift to not be at the root of the repository
- clock.sleep(for:)
- Add Equatable and Hashable conformance to String views
- Selective control of implicit copying behavior: `take`, `borrow`, `copy`
- Allow opening an existential argument to an optional parameter
- Template for a possible future object model
Other Developments worth Mentioning
It's great to see that there are plans to implement parts of the Swift compiler in Swift (I know, it sounds like an infinite recursion to use Swift for Swift, but it works because the latest development Swift is built with the latest stable release Swift, which was at some point built without any Swift involved [mostly C++]).
The 6 currently existing workgroups (Language, C++ Interoperability, Server, Swift.org website, Documentation, and Diversity) might soon be joined by new groups: Besides Numerical/ML mentioned in the May issue already, one for Swift Tooling and one for broader Platform Support were recently discussed. I also found an older discussion about an Embedded/Bare Metal/Low Resources group. All of these groups make a lot of sense to me, so I hope they will be successful!
That’s it from my September update!
A native Mac app that integrates with Xcode to help translate your app.
Get it now to save time during development & make localization easy.