[swift-evolution] [Draft] Change @noreturn to unconstructible return type

Brent Royal-Gordon brent at architechies.com
Mon Jun 6 03:12:06 CDT 2016


> The following names were suggested: NoReturn, Bottom, None, Never.
> I would pick None, because it looks like opposite to Any and fits nicely in generic types.

I discussed all of these options and more. The issue I see with None is that it could easily be interpreted as Void to those without a C background. (Actually, it's probably the most *natural* name for what we call Void.) `func x() -> None` reads like it returns nothing. `func x() -> Never` reads like it does not return.

> I would prefer the type to be simple, and be implemented as a case-less enum (not a bottom value, as in Haskell).
> 
> None should be a usual enum, with no compiler magic except that functions returning None are equivalent to current @noreturn.

Could you elaborate on this? I think it would be really useful to have a bottom type—useful to the point that, within minutes of deciding to think of examples, I quickly came up with some really good ones. Why don't you like having a bottom type?

> Example 1.
> let x: None?
> // ...
> let y = x!
> 
> It will trap in runtime not because we discover scary bottom thing, as in Haskell, but because x had value Optional.none at that moment and we asserted otherwise.

I'm not sure what you mean by this. There is no "scary bottom thing"; Bottom or None or Never or whatever you call it is still an empty type, and the unwrap still fails because the value is `.none`, not `.some`. The only difference is that you can say something like `let total = basicBooks + fatalError("implement pro books counting")` in an expression and it will compile, since Bottom/None/Never is a subtype of `basicBooks`'s type—it's simply one that will never actually be created.

I wonder if perhaps your experience with Haskell has given you a false impression of how this would work in Swift. Haskell is a pervasively lazy language. Every operation in Haskell is lazy and you have little control over when anything executes. Because of this, `undefined` values can persist for long periods of time and spread deep into your code. But Swift is not pervasively lazy; it is (relatively) simple and obvious to figure out when a given piece of code will run. When you run a Bottom/None/Never-returning expression, it does not return. Period. There is no "it does not return six files away from where you wrote it".

> We could prove that it is always true in this case, but compiler must be stupid about this.

Why? The compiler flags an error if it can statically prove a trapping overflow will occur. Why shouldn't it flag an error if it can statically prove a force unwrap will fail?

> Example 2.
> Compiler should allow including None in structures. Error will show up in constructor, when we will not be able to initialize the field.

Well, you can assign the field from a call to something like `fatalError()`, but of course that will crash at runtime. (That's true whether we use a bottom type or an empty type, by the way.)

> Example 3.
> None in an enum case makes that case never appear in values of such a type. But compiler can not know about that.

Again: Why? If one of the associated values is Bottom/None/Never, how is my code improved by the fact that I still need a case for it in a `switch` statement? What's the advantage of not being able to eliminate dead code, or call out code that will inevitably fail? We already want these things for other uses of Bottom/None/Never, like dead code elimination after a precondition fails. Why not here?

-- 
Brent Royal-Gordon
Architechies



More information about the swift-evolution mailing list