[Pitch] Conform Never to Identifiable

Conform Never to Identifiable

Introduction

This proposal conforms Never to Identifiable to make it usable as a "bottom type" for generic constraints that require Identifiable.

Motivation and Proposed Solution

With the acceptance of SE-0215, Never was deemed as being a “blessed bottom type”, but that it wouldn’t implicitly conform to all protocols—instead explicit conformance would be added where valuable.

The conformance of Never to Equatable and Hashable in SE-0215 was motivated by examples like using Never as a generic constraint in types like Result and in enumerations. These same use cases motivate the conformance of Never to Identifiable, which is pervasive in commonly used frameworks like SwiftUI.

For example, the new TableRowContent protocol in SwiftUI follows a "recursive type pattern" and has the need for a primitive bottom type with an Identifiable assocated type:

extension Never: TableRowContent {
  public typealias TableRowBody /* conforms to TableRowContent */ = Never
  public typealias TableRowValue /* conforms to Identifiable */ = Never
}

Detailed design

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
extension Never: Identifiable {
  public var id: Never {
    switch self {}
  }
}

Source compatibility

If another module has already conformed Never to Identifiable, the compiler will emit a warning:

MyFile.swift: warning: conformance of 'Never' to protocol 'Identifiable' was already stated in the type's module 'Swift'
extension Never: Identifiable { 
                 ^
MyFile.swift: note: property 'id' will not be used to satisfy the conformance to 'Identifiable'
    var id: Never {
        ^

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.

Effect on ABI stability

This change is additive.

Effect on API resilience

As this change adds new ABI, it cannot be removed in the future without breaking the ABI.

Alternatives considered

Add additional "missing" conformances to Never (e.g. CaseIterable) and other common types

A more thorough audit of "missing" conformances is called for. With this proposal we chose the narrowest possible scope in order to prioritize the addition of important functionality in a timely manner.

14 Likes

Looks good to me! I love using the "recursive type pattern" for cases where my solution has to have some special-cased primitive types, but the API shouldn't expose them as such.

A minor suggestion, though:

Even though it won't make a difference at run-time, I think a more on-point and expressive way of implementing it would be:

@available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *)
extension Never: Identifiable {
  public var id: Never { 
    switch self { }
  }
}
5 Likes

This looks totally reasonable to me.

Doug

+1 totally makes sense

You probably mean var id: Never here.

Other than that typo, this looks great!

1 Like

I think it’s worth pointing out that SE-0215 didn’t say Never shouldn’t conform to all protocols. It simply noted that making Never conform to all protocols would be a lot of work, and doing so was not part of that proposal.

5 Likes

+1.

+1, looks good to me!

Thanks @epam-gor-gyolchanyan, @beccadax, and @Nevin! I made edits to the pitch, proposal, and/or implementation based on your feedback.

1 Like

Thanks for writing this pitch, many development teams need these improvements.

From my own experience the following protocols should be additionally conformed by Never:

CaseIterable
CustomStringConvertible
CustomDebugStringConvertible
LosslessStringConvertible
LocalizedError

Does anyone have cases when conformance of FixedWidthInteger, FloatingPoint, or Collection is needed in practical tasks?

LosslessStringConvertible is a poor candidate for this because it has an initializer. Pretty much any protocol with static members or initializers is iffy. (CaseIterable is workable because allCases can just return an empty collection.)

4 Likes

Thanks for reply. With LosslessStringConvertible we can do

extension Never: LosslessStringConvertible {
  public init?(_ description: String) {
    return nil
  }
}

But I agree here, It is not always what we exactly want.

1 Like

To be a proper bottom type, I suppose it should also be the base type of any class (although this can obviously not happen by normal means)? Maybe that matters much less than for protocols in practice.