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

Антон Жилин antonyzhilin at gmail.com
Mon Jun 6 03:50:17 CDT 2016


> 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?
> 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?

Nice points! I tried to keep things as simple as possible, but it looks
like such special cases would really help here.

> 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 need Never to be subtype of any type, including structs and final
classes, but it's difficult to wrap my head around this while we don't have
subtypes in Swift.
Anyway, I see little to no problems with this. Just the proposal is going
to become longer than I expected :)

- Anton

2016-06-06 11:12 GMT+03:00 Brent Royal-Gordon <brent at architechies.com>:

> > 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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160606/18cefeb9/attachment.html>


More information about the swift-evolution mailing list