New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal]: Add Result<T> To Standard Library #757

Closed
wants to merge 4 commits into
base: master
from

Conversation

Projects
None yet
9 participants
@jshier
Contributor

jshier commented Nov 2, 2017

Add a proposal for adding Result<T>.

@NachoSoto

This comment has been minimized.

NachoSoto commented Nov 2, 2017

Why would T be parametrized but not Error? IMO applying the same logic should lead to defining Result as:

enum Result {
  case success(Any)
  case failure(Error)
}

Which is obviously not a smart idea. You can find an existing implementation of Result here which has been very successful (I personally use it in all my projects):

enum Result<Value, Error: Swift.Error> {
	case success(Value)
	case failure(Error)
}
@ianpartridge

This comment has been minimized.

Contributor

ianpartridge commented Nov 2, 2017

This is premature given the ongoing discussions around perhaps adding coroutines to the language, which could be used to build futures etc.

@jshier

This comment has been minimized.

Contributor

jshier commented Nov 2, 2017

@NachoSoto If you read the proposal, that's discussed. Like you said, no parameterization of Value is silly, as it breaks pretty much the entire use of the type. No parameterization of Error merely works around the fact that Error can't conform to itself, which means that anyone using Result<T, Error> would need an explicit error type. Given that many frameworks, especially Apple's, can return any number of error types from the underlying framework, users would be forced to cast to types they don't necessarily want, like NSError. That, and the fact that Error has useful properties without a concrete type, means that we chose not to parameterize the error.

@ianpartridge We have no real idea what that proposal will look like, so calling this premature is, in fact, itself premature. But besides that, there are a variety of other use cases for Result outside of asynchronous usage.

@jckarter

This comment has been minimized.

Member

jckarter commented Nov 2, 2017

When we previously discussed including a result type in the standard library, we had trouble coming up with any concrete use cases beyond plumbing errors across suspended continuations, which would become unnecessary once first-class suspendable functions are a thing, so we ultimately decided against including it.

@slavapestov

This comment has been minimized.

Member

slavapestov commented Nov 2, 2017

@jckarter I don't really follow the reasoning here -- should we also remove AnyIterator / AnySequence / etc, because one day generalized existentials will make them unnecessary?

@jckarter

This comment has been minimized.

Member

jckarter commented Nov 2, 2017

Those can be readily replaced by existential types once they're supported. Moving Result-based APIs to more idiomatic APIs would be a nontrivial transformation by comparison, and might not be possible automatically in all cases. Having one idiom to migrate will be enough work, without having to deal with both the existing Cocoa patterns and Result.

@JRHeaton

This comment has been minimized.

JRHeaton commented Nov 2, 2017

Hi all. I've been wanting a Result type in the Swift stdlib for some time now, and I'm also on the side advocating for typed errors being a possibility. I understand the issue described by @jshier about cases where you do want an untyped error, and Swift.Error can't conform to itself.

Would something like this work as a solution?

struct UntypedError: Swift.Error {
    let error: Swift.Error
    init(_ error: Swift.Error) {
        self.error = error
    }
}

And then when an untyped error is needed:

let untypedResult = Result<T, UntypedError>.failure(UntypedError(anyError))
@jshier

This comment has been minimized.

Contributor

jshier commented Nov 3, 2017

@JRHeaton

This comment has been minimized.

JRHeaton commented Nov 3, 2017

That’s the sort of awkwardness not typing the error avoids in the first place.

I think it's also awkward to not even know what kind of errors can come from an operation, personally.

You’d have to use it every time you consume an API that doesn’t document what error types it can return.

Yes, you would have to be explicit about the fact that you're not caring about the error type. IMO, from the perspective of a safe language, opting out of specific error handling seems safer than ignoring it by default and missing out on the ability of the compiler to help you be exhaustive with error handling (where reasonable – in other words, both ways should be possible).

By not having the error type you can capture any Error from any API, and, if you know the type, cast it back out again if you need the particular type.

You can still use untyped APIs with the explicit UntypedError approach. With Result<T>, it's impossible to use a concrete error type if you desire. With Result<T, E> and this explicit type, it's possible to do both. That seems like a win for both sides.

Additionally, since Error contains useful information by itself (a description and such), even just the plain Error value can be of use without any additional effort. I rather the extra work be on the consuming side, since that detail is optional, than the creating side, since you’ll always need an error instance of some type.

The problem with having it on the consuming side, which is part of the argument for typed throws as well, is that the consuming side can't possibly know what the internal logic of some function in another (compiled) module is, and thus, what it throws (or rethrows).

@jshier

This comment has been minimized.

Contributor

jshier commented Nov 3, 2017

If AnyError were part of the standard library already, this would probably be a different discussion. However, the lack of that type, and the general awkwardness around using such a type, seem to necessitate a more conservative Result<T> design for general usage in the standard library. For example, interacting with any Apple framework essentially requires the use of an untyped Error. Interacting with any API with undocumented errors would as well. Even in your own code, interacting with nesting throwing functions with different error types could lead to that conclusion as well. However, nothing about Result<T> ignores errors. Result<T, E> is no safer than Result<T>. Result<T> merely places the burden for error typing on the extractor rather than the wrapper. Underneath that Error value your custom error types are a simple cast away. In fact, I usually wrap my common error casts in a convenience property on Error, so I can easily access it like this: result.error?.asMyError and treat it essentially the same as if I'd had a typed error in the first place.

@srdanrasic

This comment has been minimized.

srdanrasic commented Nov 3, 2017

I personally, and I've seen a lot of other people and projects do the same, use Result<T, E> almost exclusively to get typed errors. The decision of Swift team to go with untyped errors is justifiable due to the vast number of legacy stuff they had to support and had been practical at the time, but going forward I think that Swift should not introduce more roadblocks to typed errors. If anything, it should provide us with the ways to opt-in for typed errors.

If Result<T> would be accepted it would further cement us into the world of untyped errors so I would rather prefer not having Result type in the standard library at all than having Result<T>.

@tkremenek

This comment has been minimized.

Member

tkremenek commented Nov 3, 2017

There is a lot of discussion in this pull request that should instead be on the swift-evolution mailing list.

@NachoSoto

This comment has been minimized.

NachoSoto commented Nov 3, 2017

There is a lot of discussion in this pull request that should instead be on the swift-evolution mailing list.

I'll speak for myself, but I best most people would agree with me: I don't use the mailing lists because the format is not approachable at all. What ever happened to moving them to a more modern platform?

@inamiy

This comment has been minimized.

inamiy commented Nov 3, 2017

I wrote a short poem for why I prefer typed error if you are interested: https://gist.github.com/inamiy/e3f5f7524f1bcdc582c2ff8dbbba58dd

@tkremenek

This comment has been minimized.

Member

tkremenek commented Nov 3, 2017

I'll speak for myself, but I best most people would agree with me: I don't use the mailing lists because the format is not approachable at all. What ever happened to moving them to a more modern platform?

I'm not happy about the delay of moving to Discourse either. Things took longer than expected on the bureaucratic side with unforeseen delays, but they are moving forward. In the meantime, however, let's continue having the discussions with the rest of the community where they are intended to happen. Having this conversation in the pull request is keeping people who aren't watching out of the discussion.

@inamiy

This comment has been minimized.

inamiy commented Nov 8, 2018

Super happy to see apple/swift#19982 with typed error 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment