Hello,
I have two questions regarding the transitions from P
to any P
, from Swift 5.5, to 5.6, to 6.
The first question is about applications (code bases that support a single build environment), and the second about libraries (code bases that support multiple build environments).
From the point of view of applications, I only consider the transition from Swift 5.5 to Swift 5.6, the release which introduces warnings about naked P
existentials. Do we have an idea about the eventual fixits that will come with this warning?
From the point of view of libraries, things are more complicated. Libraries love to support multiple language versions, because dropping support for a language version is, strictly speaking, a breaking change in semantic versioning. Practically speaking, libraries relax this rule, but still generally support a range of compiler/swift versions (examples: SwiftNIO, Alamofire, GRDB). The compiler helps users upgrade their compiler version without upgrading their libraries, thanks to language modes (so that, say, a Swift 6 user can depend on a library that requires the Swift 5 mode).
Libraries like to support the latest language version, so that they can improve their api with the new shiny language features. The compiler helps libraries support users who still run older language versions with conditional compilation (#if swift
and #if compiler
).
In the particular case of any P
, though, conditional compilation will NOT play its usual role, because the code below is not valid Swift code:
/// Doc comment
#if compiler(>=5.6)
public def foo(_ x: any P)
#else
public def foo(_ x: P)
#endif
{
// function body
}
A potential workaround is to define a typealias, but this extra indirection is very bad for the api surface and the library documentation:
#if compiler(>=5.6)
/// Doc comment
public typealias PExistential = any P
#else
/// Doc comment
public typealias PExistential = P
#endif
/// Doc comment
public def foo(_ x: PExistential) {
// function body
}
Another potential workaround which preserves the api surface and library documentation is private helper functions. Caveats are that we have to duplicate doc comments, and that this technique would be required for all public apis that have existentials in their signature:
#if compiler(>=5.6)
/// Doc comment
public def foo(_ x: any P) { _foo(x) }
private typealias PExistential = any P
#else
/// Doc comment
public def foo(_ x: P) { _foo(x) }
private typealias PExistential = P
#endif
private func _foo(_x: PExistential) {
// function body
}
And what if libraries do not want to enter this compatibility hell? Their only choice is to drop Swift 5.5 from their range of supported versions, and only use any P
.
So here here is my final question: Should we amend the proposal in order to help libraries that want to support Swift 5.5...6? SwiftNIO is such a library, if I understand their intent correctly, and the "2 immediately prior non-patch versions" before Swift 6 are 5.5 and 5.6. And there likely exists other libraries in this case.
EDIT: Let's imagine a library which decides to not use the complex compatibility techniques described above. It will have to go through those steps:
- Library version M supports Swift up to version 5.5. It spells existentials
P
, and it can't use Swift 5.6 features (or it would get compiler warnings on nakedP
existentials). -
After Swift 6 ships, library version M+1 ships, which supports Swift up to version 6, and does not support Swift 5.5. It spells existentials
P
, and it can use Swift 6 features.
I assume it can't drop support Swift 5.5 support before Swift 6, due to the constraints of semantic versioning and the reasonable user expectations about the range of supported language versions.
See how the library can not use Swift 5.6 features until Swift 6 is out. That's a long period of time.
I can't see drawbacks for the user (except library apis that do not use Swift 5.6 features), but maybe I did not look long enough. I encourage other pairs of eyes to take a serious look at this issue.