SE-0235 - Add Result to the Standard Library

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.

6 Likes

I know this is late, but I'm changing my response to -1. I tried for fun to integrate antitypical/Result into PromiseKit and because the Result type requires a concrete Error type to be declared it made it impossible to use this form of Result.

I believe this was acknowledged in the thread with a potential solution, but it is not acknowledged in the proposal.

I don't see the point in adding a Result type to the stdlib if projects out there that need it cannot use it as proposed.

Further using antitypical’s Result was continuously tedious due to the need to always declare which Error was involved. Sometimes it seemed impossible to use it since a particular area I needed may return multiple types of error.

I think adding as proposed wouldn't help nearly as many people or projects as people have implied here.

4 Likes

Those sorts of difficulties are precisely why the proposal is unconstrained on the error type, making definitions that just expect Error possible, where they weren't with the constrained version.

4 Likes
  • What is your evaluation of the proposal?

+1 (but more like +0.5)

  • Is the problem being addressed significant enough to warrant a change to Swift?

I think it should go further. Just adding Result is not enough, it only extends the "old" error handling besides try/catch. Both error handling methods should work together:

let foo: () throws -> String = ...
let result: Result<String, Error> = try?? foo()
// try??, or catch, or new keyword - no do { } catch required!
throw? result
// throws if result is in error state
  • Does this proposal fit well with the feel and direction of Swift?

Assuming that try/catch defined the direction: No.
But I do not think that try/catch is a good direction so I think the try-to-result-solution would improve the direction.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Given the number of custom Result types in libraries (guilty myself), it obviously is a good thing to have. Most types I had seen do not have anything beyond status (success/error), union { value, error }.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading.

Agree.

I would be +1 if Result was tightly integrated with throws:

func getX() throws -> Int { ... }

let a = try? getX() // Optional<Int>
let b: Result = try? getX() // Result<Int>

This would be very similar to current:

let a = [1, 2, 3] // Array<Int>
let b: Set = [1, 2, 3] // Set<Int>
2 Likes

The core team has discussed this proposal and agreed to revise it. The revisions in this case are sufficiently complex that we've decided that we need to put the revised proposal back out for a second round of review. I've listed the revisions in the new review thread and will be closing this one.

2 Likes