[swift-evolution] [Proposal] Typed throws

Anton Zhilin antonyzhilin at gmail.com
Mon Feb 20 11:14:13 CST 2017

2017-02-20 18:23 GMT+03:00 Matthew Johnson <matthew at anandabits.com>:

> On Feb 20, 2017, at 3:58 AM, Anton Zhilin <antonyzhilin at gmail.com> wrote:
> But that raises another concern. In a previous discussion, it was taken
> for granted that Never should conform to all protocols
> Do you have a pointer to this discussion?  I must have missed it.
is the discussion where the idea of “empty” type originated.
Some messages on the topic ended up being there

This <http://discourse.natecook.com/t/idea-repurpose-void/1406> is the
earliest mention of usage of this empty type for rethrows I could find.
Some related messages are here
as well.

We called this type NoReturn and meant it to be the *bottom type*, i.e.
subtype of all types, meaning that if you have an instance of NoReturn—which
can only happen in unreachable sections of code—then you can convert it to
any type. It should have worked like this:

func fatalError() -> Never

func divide(a: Int, b: Int) -> Int {
    if b == 0 {
        let n: Never = fatalError()
        return n as Int
    return a / b

I pushed the idea of replacing rethrows with Never, inspired by Haskell.
Although Haskell doesn’t have static function requirements and initializer

, because if one obtains an instance of Never (and they won’t), then
> everything is possible. But now we say that Never can’t conform to Default,
> because this would break its very invariant. Also it can’t conform to any
> protocol with static members or initializers.
> It seems highly problematic to me to say that never conforms to any
> protocol with non-instance requirements.
Here is an example with instance requirements only:

protocol MakesPizza {
    func cook() -> Pizza
extension Never : MakesPizza {
    func cook() -> Pizza {
        // this method will never be called anyway

let maestroLaPizza = isHeAtWork ? validMaestro :
(fatalError("something went wrong") as MakesPizza)

In this way, Never can conform to any protocol with only instance

But then basically, Never trick can’t be used when we request anything more
> than Error from generic error type (with static members or initializers).
> So this approach turns out to be more limiting than rethrows.
> Can you elaborate here?  If you require a function to throw an error type
> that has non-instance requirements then you would necessarily be
> restricting callers to provide a throwing function.  It is not possible to
> express such a function with `rethrows`.  You can’t talk about the error
> type at all.  If you could talk about the error type and were able to
> constrain it in this way `rethrows` would necessarily have to exhibit the
> same behavior as the generic version.  The behavior arises out of the
> constraint you are applying, not the mechanism by which you forward the
> type.
With rethrows approach:

protocol BaseError : Error {

func seq<E1, E2>(f: () throws(E1) -> (), g: () throws(E2) -> ())
     where E1: BaseError, E2: BaseError { ... }

With Never approach, we have to create two separate functions for the same
effect, because Never does not fit in BaseError:

func seq<E1, E2>(f: () throws(E1) -> (), g: () throws(E2) -> ())
     where E1: BaseError, E2: BaseError {
    // It never actually throws E1() or E2() itself, but this fact
can't be reflected in the signature

func seq(f: () -> (), g: () -> ()) {
    // repeat the body

That’s where loss of information (which I meantioned earlier) hurts: we
can’t apply magic and say “if E1 and E2 are Never then seq does not throw.
Because it *can* throw anyway.

Well, I’m just repeating myself, at least I gave a bit more complete
example :)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170220/619cbaba/attachment.html>

More information about the swift-evolution mailing list