SE-0235 - Add Result to the Standard Library

I see both arguments being made, by different commenters. I don't know what Xwu intended, but that's the way I read it.

I, personally, feel that the stdlib should leave semantics out when possible, and allow developers to assign them as they see fit. Thus, I am in favor of an Either type. As it stands, this proposal allows one to effectively use Result<MyError, MyData>, and really confuse things. If the type is going to be biased, it shouldn't leave room like this. Leave Error out and declare the case to be .failure(Swift.Error).

If Either types are desirable, I would argue for them to be structural types like tuples or protocol compositions, because they are just as semantically agnostic as those are, and need just as much special-casing (conversions between same types in different order, arbitrary number of types, etc.). But that’s tangential to the proposal at hand (and not something I actually support having in any form).


It allows that, but it would strongly discourage it in documentation, naming, etc. And if you saw that type you would instantly understand that something strange was going on, and perhaps MyError really was the success type somehow. Analogously, I don't think these two types are interchangeable

struct Point { var x, y: Double }
struct Dimensions { var width, height: Double }

and that you should only define one or the other, or combine them both into

struct DoublePair { var first, second: Double }

or even

struct Pair<T> { var first, second: T }

Agree, this is why I brought up OCaml polymorphic variants earlier. If we have structural sums, extensions on structural types and protocol conformances for structural types I can't think of a good reason to have an Either at all. Nobody in the Either camp has given a substantive rationale for why we should introduce it instead of or in addition to language features like that. The only possible argument I can think of is not a good one: that Either could theoretically still be added to Swift 5 while language features would take more time to design and implement.


Thanks for the additional examples and detail in your two recent posts. Between these and the post from @xwu above, I have a much better understanding of the desire for less abstract “Result” semantics rather than an unbiased “Either”, and I see the value.

So this changes my initial -1 on Result vs. Either to a +1, despite it still not feeling quite like a real citizen of Swift and the Apple frameworks ecosystem. It does at least represent a commonly used semantic + structural abstraction that’s better off as a common definition.

1 Like

While I agree that row types would be ideal, language features like that seem so far off in the distance that we may never see them. Swift is product-typed biased right now, plain and simple. Tuples have no sum-based equivalent. Key paths only work with structs out-of-the-box. Enums are far less ergonomic than structs and a general Either type, while not ideal, is certainly better than nothing at all or waiting forever. I also fear that if/when we get row polymorphism, it'd be another feature that only works with product types.


In the spirit of keeping things focused to the original topic in this already very long review thread, might I propose that we move discussion of structural types and ‘Either’ out to another thread?


Either is part of the discussion, no? As it's mentioned in the proposal.

1 Like

Since this thread is a review of a specific proposal for a ‘Result’ type, the question of how to design an ‘Either’ type and whether it is best a structural type is pretty far afield, I should think.


I agree with Xiaodi. If your opinion is that we should have a generic Either type over a "biased" Result type, that's totally reasonable feedback to leave here, but this is not the place to debate an actual design for an Either type or a general structural-sums feature.

Also, the review period for this proposal is technically over.


I suggest we all take a step back and return to the original proposal - Result. Either is a nice type and I'd like to have it at my disposal, but is it in conflict with the Result? No, it is another type. It is a more generalized version of Result. It does not conflict with Result. Focus on the semantics. Semantics of the Result type is well-known. Either I get a value T or an error E: Error. This is different from Either where I expect either a value T or another value U. Since semantics is so clear in these cases, the only way it makes sense for Result to be implemented is to have error constrained Error: Swift.Error - otherwise we are talking about Either type and that's not a topic of this proposal.

Is Result a backdoor to introducing typed throws? It is such a harsh way to put it, but even if it is, is that really a bad thing? Is allowing typed throws necessarily a bad thing? There are so many use cases where typed errors are useful so why not allow them. They don't have to be forced upon users, but they could be allowed. The way I see this, is that there is a pressing need for Result type. It's solves so many problems that the Swift community is experiencing today. It is potentially a building block of a better error handling system, that may or may not come. It allows us to make our code more expressive.

Could it become obsolete when we get better concurrency support? It is likely that its usage will drop significantly. But will it conflict will anything, will it limit us to do better? No.

What I loved about early days of Swift is that there was no huge fear of change. I understand that there is so much more responsibility these days, but openness to new things is what got Swift to this point of being a great language to work with. I would hate to see it stagnate.

It looks like you already switched your position here but I wanted to comment on the Either<Former, Latter> . I was also on the same camp when it came to naming but your comments that Either<Former, Latter> and Either<Latter, Former> should be equivalent solidify my stance on the general Either type.

There are cases when I do want to throw the error away. In my mind a Result's type main benefit is that is able to carry an error but I should be able to ignore that error if I do not care about it.

Consider this:

func fetchNewestError(fromRemoteServerLogAt url: URL,
    completion: (Either<Error, Error>) -> ())

Which case of the Either represents the newest error, successfully fetched, and which branch represents a failure to access the remote server log?

Contrast with

func fetchNewestError(fromRemoteServerLogAt url: URL,
    completion: (Result<Error, Error>) -> ())

Here it is IMHO obvious that the .success case represents the fetched error, and the .failure case represents an error accessing the remote log.

I don't like Either because it is too generic. Case names like left/right, former/latter, and first/second are almost never relevant in an application of Either.


The migrator's primary and most important goal is to preserve the semantics of the program when moving to latest version. I'm not in favor of trying to adjust uses of Result from multiple third-party libraries, particularly when considering that it could be prohibitively difficult to be 100% accurate due to the stdlib one not being a drop-in replacement, and the fact that we may not have access to downstream clients of this APIs using the third-party Result.

We could potentially offer a separate "modernization" pass, similar to the ObjC modernizer, which can be opt-in and independent of moving to latest version (you could run it anytime after a project already moved to swift 5+), but I would recommend not to base decisions on the existence of such functionality.


Okay, thanks for the feedback.

FWIW I think antitypical/Result would do what it could to help:

  1. Use an #if swift check to only define Result if it wasn't available in the standard library
  2. Use @available unavailable renames for anything that has a different name in an accepted proposal

IME those steps make migration easy.


+1 for adding a Result type

1 Like

...because that's not the purpose of this review.

1 Like

To the extent that people have advocated Either instead of Result I think it would have been on topic. But obviously the review period is now over.

1 Like

Although the review period is over, please let me leave my comment. I have been thinking about it for days.

If I correctly understood what you intended in the comment below you linked,

it seems to suggest that JSONDecoder.decode(_:from:) returns Results instead of throwing errors.

I think usefulness of automatic propagation (throws) is not limited to "systemic errors". For example, if we have an API to decode JSONs manually, and if manual propagation (Result) is preferred for non "systemic errors", the API would return Results like JSONDecoder.decode(_:from:). Then decoding JSONs manually would be written like below with complex conversions.

func decodeFoo(from json: JSON) -> Result<Foo, DecodingError> {
    return Result {
        let a: Int = try json["a"].unwrapped()
        let b: Bool = try json["b"].unwrapped()
        let c: String = try json["c"].unwrapped()
        return Foo(a: a, b: b, c: c)
    }.mapError { $0 as! DecodingError }

If the API throws errors directly, it can be written much more simply as follows (assuming that throwing subscripts referred in SE-0148 are available).

func decodeFoo(from json: JSON) throws -> Foo {
    let a: Int = try json["a"]
    let b: Bool = try json["b"]
    let c: String = try json["c"]
    return Foo(a: a, b: b, c: c)

As seen in the example above, I think automatic propagation is also useful for non "systemic errors". So I prefer using Result more limitedly as @John_McCall commented.

Then I think it is better not to add Result to the standard library now. If Result is added to the standard library now, I am sure that it will be widely abused instead of throws. I guess 90% of actual use cases of Result today are for asynchronous operations and typed errors. Because async/await and typed throws are not supported currently, Result will be the only way for the cases. If async/await and typed throws are introduced first (though I am not sure if typed throws should be supported), before Result, async/await and typed throws would be used in the cases. And the community could get experienced with them. Then Result could be used properly only when manual propagation is required.

Terms of Service

Privacy Policy

Cookie Policy