Conform Never to Equatable and Hashable

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 :slight_smile:.

[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.

3 Likes

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.

6 Likes

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.

7 Likes

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.

7 Likes

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.

4 Likes

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.

1 Like

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. :heart_eyes:

5 Likes

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!

1 Like