Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager or direct message in the Swift forums).
What goes into a review of a proposal?
The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift.
When reviewing a proposal, here are some questions to consider:
What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
Thank you for helping improve the Swift programming language and ecosystem.
As the warning notes, the new conformance will be used to satisfy the protocol requirement. This difference shouldn't present an observable difference given that an instance of Never cannot be constructed.
There is a difference if an existing module declares an ID type which is not Never. The proposal, as written, would break such existing code.
IIRC, previous proposals of the same kind did preserve existing conformances instead of replacing them with the one of the standard library. Maybe someone has a better recollection than mine, and could point to such a precedent?
However, this continues to highlight a significant design point we need to look at: Never should implicitly conform to a bunch of protocols. Having one-off swift evolution proposals for each of them doesn't really make sense, we should have a systematic approach.
But I guess I'll ask the elephant in the room question -- why not look into making Never a true bottom type? It always seemed weird to me that Swift lacked a true bottom type and Never seemed like the natural candidate for it.
This does not block the current proposal from being accepted, just wanted to see if there's any thoughts we can share here?
Never should become a blessed bottom type in the language. This matches with semantics in other languages and its intended role in Swift.
With respect to the latter, the Core Team discussed what Never being a bottom type actually meant. From that discussion we reached the following conclusions:
Semantically, as a bottom type, it would mean that Never should be implicitly convertible to any other type. This composes well in the type system because of the fact that instances of Never cannot be instantiated.
However, being a bottom type does not imply that Never should implicitly conform to all protocols. Instead, convenient protocol conformances for Never should be added as deemed useful or necessary.
There are various details to suss out with making Never a bottom type, and thus making it a bottom type would be served by a separate and well-considered proposal . It is the opinion of the Core Team that making Never a bottom type would be separate from it conforming to all protocols, and thus the motivation of this particular proposal and the particular problems it addresses stands on its own.
With respect to protocol conformances, the Core Team felt the language has clearly moved in a direction where explicit protocol conformance is fundamental to the language model. The compiler has grown affordances to make the implementation of protocol conformances easy in many cases (e.g., synthesized implementations of Hashable ) but that the explicit protocol conformance is quite important. Adding rules for implicit protocol conformances ā something that has been considered in Swiftās history ā ends up adding real complexity to the language and its implementation that can be hard to reason about by a user as well as by the language implementation.
(which is not the say that that's the end of the discussion, but just the latest official statement from the Core Team. Note of course the explicit invitation for a "separate and well-considered proposal" to discuss further)
Perhaps that approach should start with a list of all protocols in the Swift Standard Library and solicit any objections to conforming Never to each of them?
From the compilerās point of view, I would assume that since no runtime values ever belong to it, it does not need a witness table or any other kind of runtime support other than a metatype. In type checking, it would form a unique āalways trueā special case, one that does not require any sort of more general implicit conformance mechanism.
From the language userās point of view, Iām hard-pressed to think of any logical reason why any protocol would not want Never to conform to it.
Iām sure thereās an excellent rationale here! It just eludes me. As it stands, Chrisās and Konradās remarks echo my sentiments exactly:
If making Never a true bottom type, including all protocols, is off the table, then at least this:
Ah, and thus var p: P = Never(ā¦) should logically compile, which isā¦not nice.
And then thereās the problem of conflicting protocol requirements:
protocol P0 { var x: Int { get } }
protocol P1 { var x: String { get } }
// How can Never conform to both?
It seems to me it might be possible to get around both these problems by prohibiting access to all protocol requirements ā initializers, static and instance members, everything ā via the naked Never type:
func f(p0: P0, never: Never) {
p0.x // allowed
never.x // not allowed, even though Never conforms to P0
}
Hard to shake the feeling thereās a solution lurking around the corner here. However, I do now see how this becomes a rabbit hole, thanks to your comment!
What would the pitfall of this be in practice? The Never type is unoccupied, so e.g. switching on the runtime type of a value canāt ever hit an unexpected non-match:
protocol P { }
struct A: P { }
struct B: P { }
extension Never: P {}
func f(_ p: P) {
switch p {
case is A: print("a")
case is B: print("b")
default: fatalError("unreachable") // This is in fact unreachable, even though Never: P
}
}
Hmm, I suppose one could gum things up by switching on the metatype of a presumed-exhaustive protocol:
That's what I was thinking of in particular. I don't have any ideas of what one would switch on the metatype for, but it works, and making Never conform to such protocols would change the behavior.
What about the disguised Never type? Recall that types can be materialized in any generic function:
protocol DefaultInitializable {
init()
}
func instantiate<T: DefaultInitializable>() -> T {
return T()
}
let n: Never = instantiate()
The same problem applies to all static methods and properties (not just Self-constrained or otherwise āfancyā ones).
One could work around this by fatalErroring, or maybe returning nil for nullable initializers, but this strikes me as unprincipled; depending on your point of view, itās either turning an error that should be static into a dynamic one, or implicitly adding ad-hoc behaviour to the bottom type.
It would also require the static parts of protocol conformances to actually be synthesized for Never (either up front or through special runtime synthesis), whereas use of Never as a subtype of a concrete type should never incur overhead.