Never
“Never” is a proposition that an event doesn’t occur at any time in the past or future. It’s logical impossibility with a time axis; nothingness stretching out in all directions, forever.
…which is why it’s especially worrisome to encounter this comment in code:
// this will never happen
Every compiler textbook will tell you that a comment like this one can’t and won’t affect the behavior of compiled code. Murphy’s Law says otherwise.
How does Swift keep us safe in the unpredictable chaos that is programming? You’ll never believe the answer: nothing and crashing.
Never
was proposed as a replacement for the @noreturn
attribute in
SE-0102: “Remove @noreturn attribute and introduce an empty Never type”,
by Joe Groff.
Prior to Swift 3,
functions that stop execution, like
fatal
,
abort()
, and
exit(_:)
,
were annotated with the @noreturn
attribute,
which told the compiler that there was no return to the call site.
// Swift < 3.0
@noreturn func fatal Error(_ message: () -> String = String(),
file: Static String = #file,
line: UInt = #line)
After the change,
fatal
and its trapping cohorts
were declared to return the Never
type:
// Swift >= 3.0
func fatal Error(_ message: @autoclosure () -> String = String(),
file: Static String = #file,
line: UInt = #line) -> Never
For a type to replace the functionality of an annotation,
it must be pretty complex, right?
Nope! Actually, just the opposite —
Never
is arguably the simplest type in the entire Swift standard library:
enum Never {}
Uninhabited Types
Never
is an uninhabited type,
which means that it has no values.
Or to put it another way, uninhabited types can’t be constructed.
Enumerations with no cases are the most common example of an uninhabited type in Swift. Unlike structures or classes, enumerations don’t receive an initializer. And unlike protocols, enumerations are concrete types that can have properties, methods, generic constraints, and nested types. Because of this, uninhabited enumeration types are used throughout the Swift standard library to do things like namespace functionality and reason about types.
But Never
isn’t like that.
It doesn’t have any fancy bells or whistles.
It’s special by virtue of it being what it is (or rather, isn’t).
Consider a function declared to return an uninhabited type: Because uninhabited types don’t have any values, the function can’t return normally. (Because how could they?) Instead, the function must either stop execution or run indefinitely.
Eliminating Impossible States in Generic Types
Sure, this is interesting from a theoretical perspective,
but what practical use does Never
have for us?
Not much — or at least not before the acceptance SE-0215: Conform Never to Equatable and Hashable.
In his proposal,
Matt Diephouse explains the motivation behind
conforming this obscure type to Equatable
and other protocols this way:
Never
is very useful for representing impossible code. Most people are familiar with it as the return type of functions likefatal
, butError Never
is also useful when working with generic classes. For example, aResult
type might useNever
for itsValue
to represent something that always errors or useNever
for itsError
to represent something that never errors.
Swift doesn’t have a standard Result
type,
but most of them look something like this:
enum Result<Value, Error> {
case success(Value)
case failure(Error)
}
Result
types are used to encapsulate values and errors
produced by functions that execute asynchronously
(whereas synchronous functions can use throws
to communicate errors).
For example,
a function that makes an asynchronous HTTP request might use a Result
type
to wrap either a response and data or an error:
func fetch(_ request: URLRequest,
completion: (Result<(URLResponse, Data), Error>) -> Void) {
…
}
When calling that method,
you’d switch over result
to handle .success
and .failure
separately:
fetch(request) { result in
switch result {
case let .success(response, _):
print("Success: \(response)")
case .failure(let error):
print("Failure: \(error)")
}
}
Now consider a function that’s guaranteed to always return a successful result in its completion handler:
func always Succeeds(_ completion: (Result<String, Never>) -> Void) {
completion(.success("yes!"))
}
By specifying Never
as the result’s Error
type,
we’re using the type system to signal that failure is not an option.
What’s really cool about this
is that Swift is smart enough to know that
you don’t need to handle .failure
for the switch
statement to be exhaustive:
always Succeeds { (result) in
switch result {
case .success(let string):
print(string)
}
}
You can see this effect played out to its logical extreme
in the implementation conforming Never
to Comparable
:
extension Never: Comparable {
public static func < (lhs: Never, rhs: Never) -> Bool {
switch (lhs, rhs) {}
}
}
Because Never
is an uninhabited type,
there aren’t any possible values of it.
So when we switch over lhs
and rhs
,
Swift understands that there aren’t any missing cases.
And since all cases —
of which there aren’t any —
return Bool
,
the method compiles without a problem.
Neat!
Never as a Bottom Type
As a corollary,
the original Swift Evolution proposal for Never
hints at the theoretical usefulness of the type with further enhancement:
An uninhabited type can be seen as a subtype of any other type — if evaluating an expression never produces a value, it doesn’t matter what the type of that expression is. If this were supported by the compiler, it would enable some potentially useful things…
Unwrap or Die
The forced unwrap operator (!
)
is one of the most controversial parts of Swift.
At best, it’s a necessary evil.
At worst, it’s a code smell that suggests sloppiness.
And without additional information,
it can be really tough to tell the difference between the two.
For example,
consider the following code that assumes array
to not be empty:
let array: [Int]
let first Iem = array.first!
To avoid force-unwrapping,
you could use a guard
statement with conditional assignment instead:
let array: [Int]
guard let first Item = array.first else {
fatal Error("array cannot be empty")
}
In the future,
if Never
is implemented as a bottom type,
it could be used in the right-hand side of nil-coalescing operator expression.
// Future Swift? 🔮
let first Item = array.first ?? fatal Error("array cannot be empty")
If you’re really motivated to adopt this pattern today,
you can manually overload the ??
operator thusly (however…):
func ?? <T>(lhs: T?, rhs: @autoclosure () -> Never) -> T {
switch lhs {
case let value?:
return value
case nil:
rhs()
}
}
Expressive Throw
Similarly,
if throw
is changed from being a statement
to an expression that returns Never
,
you could use throw
on the right-hand side of ??
:
// Future Swift? 🔮
let first Item = array.first ?? throw Error.empty
Typed Throws
Looking even further down the road:
If the throws
keyword in function declarations
added support for type constraints,
then the Never
type could be used to indicate that a function won’t throw
(similar to the Result
example before):
// Future Swift? 🔮
func never Throws() throws<Never> {
…
}
never Throws() // `try` unnecessary because it's guaranteed to succeed (perhaps)
Making a claim that something will never be the case can feel like an invitation for the universe to prove otherwise. Whereas modal or doxastic logics allow for face-saving compromise (“it was true at the time, or so I believed!”), temporal logic seems to hold propositions to a higher standard.
Fortunately for us,
Swift lives up to this higher standard thanks to the unlikeliest of types,
Never
.