That should not compile so you should be getting an error here, not just for the ==
comparison.
I can't help that. Blame the Playground .
[edit]
I modeled the example on the Result
type in the original post.
My point is that an error in that location would be much more useful. If you cannot construct the values at all and the compiler is clear about that you won’t be wondering why you can’t compare them.
If my example code shouldn't work, then why would the Result
type in the original post work? Would it be different because there are different cases, only one of which would be invalid with a Never
type?
Also, should I file a bug if the compiler (not just Playgrounds, which may be buggy) accepts it?
This doesn't compile because you are applying ==
to two functions, not two E
values. The type of E<Never>.e
is a function (Never) -> E<Never>
, and functions are not equatable.
OK. That is definitely confusing. As @anandabits suggests, I shouldn't be able to assign the variables in the first place. I definitely don't expect the values to be functions.
Why not? If you refer to a case
that has associated values without passing associated values, it's a function that constructs that case
when it's called with those values. This has nothing to do with Never
—it's how all enums
work.
I just learned that. And you can't assign an associated value because it's Never
. It all makes sense now.
I think I learned about 5 new things today.
As a bottom type, we could conceivably decide that Never implicitly conforms to all protocols. Adding specific useful conformances like Equatable might be a reasonable stopgap.
Shouldn't the fatal error be correct?
If this function is called it means you've constructed a value that doesn't exist which is a contradiction.
I've used in the past a "impossible" function that help reasoning about what it means to use a botton type
func impossible<A>(_ contra: Never) -> A {
fatalError("reached a contradiction")
}
And every method from every protocol that Never conforms to could be trivally implemented with
return impossible(self)
The fatalError
doesn't hurt necessarily, but if that function gets called, then it's really a failure in the compiler as opposed to some other type of failure where a precondition in buggy user's code has failed to be satisfied.
I think Never
can't conform to all protocols:
Considering the following protocols:
protocol Foo {
associatedtype A
}
protocol Bar {
static func f()
}
protocol Baz {
init()
}
Never
can't conform to them, since they have type-level, not value-level requirements. And while Never
value doesn't exist, Never
type does.
Associated types in and of themselves don't need to be a problem (they can be inferred to Never
), but static requirements that don't involve Self
could conceivably have real implementations, sure.
If they were recursively checked whether Never
can conform to them, then yeah, I didn't think of that.
By the way, if we would eventually have to choose a couple of protocols for Never
to conform to, I'd really love Error
to be one of them.
Never
and every other uninhabited type should just be a true bottom type. In type theory, the bottom type is the subtype of every type. In Swift, it’d mean it conforms to every protocol. It would also mean that a Never
-returning function is a valid value for a parameter or variable taking an arbitrary function (because the return type Never
is always a subtype of whatever expected return type) — you could essentially directly assign or pass fatalError
instead of a function returning something.
It’s probably not a trivial change though. The type checker is a complex beast in Swift!