Adding Result to the Standard Library

The Result type I’ve outlined includes the functions necessary to translate between do try catch and Result. But I fundamentally disagree with your characterization of Swift’s error handling. At most, Swift strives to hide the line between normal functions and error throwing ones, but only until the developer needs access to those errors. If you’re using an API that takes throwing functions and handles the do / catch for you, then sure, you’ll never see the result of those functions as anything more than the values you get from those APIs But anyone writing their own try / catch statements is quite clearly going to see the result/error separation that Result encapsulates. In quite a few cases, passing an encapsulation around rather than having to either mark all functions as throwing in order to propagate an error, or constantly implementing try / catch, results is code that is far easier to read, write, and reason about it. Of course it’s not an absolute solution, which is why the ability to turn a Result into a throwing function exists, but instead a complement to the current error handling in Swift.

Jon

···

On Nov 2, 2017, at 3:11 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Thu, Nov 2, 2017 at 11:58 AM Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:
  You would continue to be free to discourage the usage of Result for whatever you want. For the rest of us, Result isn’t intended to replace throws or do/catch, but provide a way to accomplish things in a much more compact and sometimes natural way. As with any API it could be used stupidly. But frankly, what developers what to do to wrap their errors is up to them.

And it still is, with the Result implementations that are available to third-parties today.

My concerns regarding Result aren't about my personal discouragement of its use, but the *reasons* why I discourage its use. The Swift language very deliberately draws a line between result outcomes that are return values and error outcomes that are thrown, and it implements not only standard library types but also language syntactic sugar to support those.

If someone wants to depend on a third-party Result<> to conflate successful outcomes and error outcomes in their own code, that's absolutely their right. But entry into the standard library has a much higher bar, and what we're talking about here is adding a feature that now would give users two disparate and incompatible ways (without explicit transformations, or other syntactic sugar) of handling errors. That makes me uneasy from the point of view of both an API designer and consumer, and just restating that it's a common pattern and people want it doesn't address those concerns.

Adding Result is just a way of blessing a result/error representation, since it has become a rather common pattern. If you’ve looked at the implementation I showed, you’ll see that there’s far more functionality than just a Result type, including API for converting back and forth from throwing functions, as well as functional transforms. Result is a complement to try do catch, not a replacement.

Jon

On Nov 2, 2017, at 2:48 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

On Thu, Nov 2, 2017 at 11:32 AM Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:
  That’s been an argument against Result for 2 years now. The usefulness of the type, even outside of whatever asynchronous language support the core team comes up with, perhaps this year, perhaps next year, is still very high. Even as something that just wraps throwing functions, or otherwise exists as a local, synchronous value, it’s still very useful as way to encapsulate the value/error pattern.

This is one of the parts that concerns me, actually. The beauty of Swift's error design is that function results denote expected/successful outcomes and thrown errors denote unexpected/erroneous outcomes. Since they are different, each is handled through its own language constructs, and since the language itself supports it (rather than being entirely type-based), you don't have the proliferation of unwrapping boilerplate that you have with Result<>.

In our own code bases, I actively discourage the use of Result<> in that way, because it tries to cram both of those concepts into the expected/successful outcomes slot in the language. For asynchronous APIs that's somewhat unavoidable today, but if that's going to change, I'd rather the language focus on a way that's consistent with other error handling already present in Swift.

Adding an API to the standard library is the core team saying "this is blessed as something around which we support APIs being designed." IMO, I'd prefer it if the language did *not* bless two disparate ways of communicating error outcomes but rather converged on one.

IMO, "things aren't happening fast enough" isn't great motivation for putting something permanently into the standard library or the language without considering the context of other things going on around it. If you're going to propose something that overlaps with asynchronous APIs, it only helps your case if you can discuss how it can integrate—rather than collide—with those efforts.

That pattern will likely never go away. Additionally, having the Result type in the standard library removes a source of conflict between all other Result implementations, which are becoming more common.

On Nov 2, 2017, at 2:26 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

Given that the Swift team is currently working on laying the groundwork for asynchronous APIs using an async/await model, which would presumably tie the throwing cases more naturally into the language than what is possible using completion-closures today, are we sure that this wouldn't duplicate any efforts there or be made obsolete through other means?

In other words, while Result<> can be a very useful foundational component on its own, I think any proposal for it can't be made in isolation, but very much needs to consider other asynchronous work going on in the language.

On Thu, Nov 2, 2017 at 11:15 AM Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
You don’t lose it, it’s just behind `Error`. You can cast out whatever strong error type you need without having to bind an entire type to it generically. If getting a common error type out happens a lot, I usually add a convenience property to `Error` to do the cast for me. Plus, having to expose an entire new error wrapper is just a non starter for me and doesn’t seem necessary, given how Result is currently used in the community.

Jon

On Nov 2, 2017, at 2:12 PM, Dave DeLong <swift@davedelong.com <mailto:swift@davedelong.com>> wrote:

I think I’d personally rather see this done with a generic error as well, like:

enum GenericResult<T, E: Error> {
  case success(T)
  case failure(E)
}

And a typealias:

typealias Result<T> = GenericResult<T, AnyError>

This would require an “AnyError” type to type-erase a specific Error, but I’ve come across many situations where a strongly-typed error is incredibly useful, and I’d be reluctant to see that thrown away.

Dave

On Nov 2, 2017, at 12:08 PM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Swift-Evolution:
  I’ve written a first draft of a proposal to add Result<T> to the standard library by directly porting the Result<T> type used in Alamofire to the standard library. I’d be happy to implement it (type and tests for free!) if someone could point me to the right place to do so. I’m not including it directly in this email, since it includes the full implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/proposals/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

The Result type I’ve outlined includes the functions necessary to
translate between do try catch and Result. But I fundamentally disagree
with your characterization of Swift’s error handling. At most, Swift
strives to hide the line between normal functions and error throwing ones,
but only until the developer needs access to those errors. If you’re using
an API that takes throwing functions and handles the do / catch for you,
then sure, you’ll never see the result of those functions as anything more
than the values you get from those APIs But anyone writing their own try /
catch statements is quite clearly going to see the result/error separation
that Result encapsulates. In quite a few cases, passing an encapsulation
around rather than having to either mark all functions as throwing in order
to propagate an error, or constantly implementing try / catch, results is
code that is far easier to read, write, and reason about it.

I would think the opposite is true.

    let value = compute()

Looking at this line of code, I have no idea whether I should expect to
handle errors or not. Does "compute" return a Result<> or just the value I
want? If we used throwing instead, it's obvious without any other context
that an error can occur there:

    let value = try compute()

That's another explicit design choice in Swift, that "try" marks
expressions so it's very clear which expressions can throw, rather than
just being a block that surrounds an arbitrarily long amount of code.
Result<> provides none of that context.

···

On Thu, Nov 2, 2017 at 12:21 PM Jon Shier <jon@jonshier.com> wrote:

Of course it’s not an absolute solution, which is why the ability to turn
a Result into a throwing function exists, but instead a complement to the
current error handling in Swift.

Jon

On Nov 2, 2017, at 3:11 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Thu, Nov 2, 2017 at 11:58 AM Jon Shier <jon@jonshier.com> wrote:

You would continue to be free to discourage the usage of Result for
whatever you want. For the rest of us, Result isn’t intended to replace
throws or do/catch, but provide a way to accomplish things in a much more
compact and sometimes natural way. As with any API it could be used
stupidly. But frankly, what developers what to do to wrap their errors is
up to them.

And it still is, with the Result implementations that are available to
third-parties today.

My concerns regarding Result aren't about my personal discouragement of
its use, but the *reasons* why I discourage its use. The Swift language
very deliberately draws a line between result outcomes that are return
values and error outcomes that are thrown, and it implements not only
standard library types but also language syntactic sugar to support those.

If someone wants to depend on a third-party Result<> to conflate
successful outcomes and error outcomes in their own code, that's absolutely
their right. But entry into the standard library has a much higher bar, and
what we're talking about here is adding a feature that now would give users
two disparate and incompatible ways (without explicit transformations, or
other syntactic sugar) of handling errors. That makes me uneasy from the
point of view of both an API designer and consumer, and just restating that
it's a common pattern and people want it doesn't address those concerns.

Adding Result is just a way of blessing a result/error representation,
since it has become a rather common pattern. If you’ve looked at the
implementation I showed, you’ll see that there’s far more functionality
than just a Result type, including API for converting back and forth from
throwing functions, as well as functional transforms. Result is a
complement to try do catch, not a replacement.

Jon

On Nov 2, 2017, at 2:48 PM, Tony Allevato <tony.allevato@gmail.com> >> wrote:

On Thu, Nov 2, 2017 at 11:32 AM Jon Shier <jon@jonshier.com> wrote:

That’s been an argument against Result for 2 years now. The usefulness
of the type, even outside of whatever asynchronous language support the
core team comes up with, perhaps this year, perhaps next year, is still
very high. Even as something that just wraps throwing functions, or
otherwise exists as a local, synchronous value, it’s still very useful as
way to encapsulate the value/error pattern.

This is one of the parts that concerns me, actually. The beauty of
Swift's error design is that function results denote expected/successful
outcomes and thrown errors denote unexpected/erroneous outcomes. Since they
are different, each is handled through its own language constructs, and
since the language itself supports it (rather than being entirely
type-based), you don't have the proliferation of unwrapping boilerplate
that you have with Result<>.

In our own code bases, I actively discourage the use of Result<> in that
way, because it tries to cram both of those concepts into the
expected/successful outcomes slot in the language. For asynchronous APIs
that's somewhat unavoidable today, but if that's going to change, I'd
rather the language focus on a way that's consistent with other error
handling already present in Swift.

Adding an API to the standard library is the core team saying "this is
blessed as something around which we support APIs being designed." IMO, I'd
prefer it if the language did *not* bless two disparate ways of
communicating error outcomes but rather converged on one.

IMO, "things aren't happening fast enough" isn't great motivation for
putting something permanently into the standard library or the language
without considering the context of other things going on around it. If
you're going to propose something that overlaps with asynchronous APIs, it
only helps your case if you can discuss how it can integrate—rather than
collide—with those efforts.

That pattern will likely never go away. Additionally, having the Result
type in the standard library removes a source of conflict between all other
Result implementations, which are becoming more common.

On Nov 2, 2017, at 2:26 PM, Tony Allevato <tony.allevato@gmail.com> >>> wrote:

Given that the Swift team is currently working on laying the groundwork
for asynchronous APIs using an async/await model, which would presumably
tie the throwing cases more naturally into the language than what is
possible using completion-closures today, are we sure that this wouldn't
duplicate any efforts there or be made obsolete through other means?

In other words, while Result<> can be a very useful foundational
component on its own, I think any proposal for it can't be made in
isolation, but very much needs to consider other asynchronous work going on
in the language.

On Thu, Nov 2, 2017 at 11:15 AM Jon Shier via swift-evolution < >>> swift-evolution@swift.org> wrote:

You don’t lose it, it’s just behind `Error`. You can cast out whatever
strong error type you need without having to bind an entire type to it
generically. If getting a common error type out happens a lot, I usually
add a convenience property to `Error` to do the cast for me. Plus, having
to expose an entire new error wrapper is just a non starter for me and
doesn’t seem necessary, given how Result is currently used in the community.

Jon

On Nov 2, 2017, at 2:12 PM, Dave DeLong <swift@davedelong.com> wrote:

I think I’d personally rather see this done with a generic error as
well, like:

enum GenericResult<T, E: Error> {
case success(T)
case failure(E)
}

And a typealias:

typealias Result<T> = GenericResult<T, AnyError>

This would require an “AnyError” type to type-erase a specific Error,
but I’ve come across many situations where a strongly-typed error is *incredibly
*useful, and I’d be reluctant to see that thrown away.

Dave

On Nov 2, 2017, at 12:08 PM, Jon Shier via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Swift-Evolution:
I’ve written a first draft of a proposal to add Result<T> to the
standard library by directly porting the Result<T> type used in Alamofire
to the standard library. I’d be happy to implement it (type and tests for
free!) if someone could point me to the right place to do so. I’m not
including it directly in this email, since it includes the full
implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/proposals/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

> I would argue that a successful addition to the standard library *must*
have such additional justification about why it needs built-in support. If
it's already in use as a third-party library, and you're arguing that the
third-party design is already quite satisfactory and there's no built-in
support required, then that's fundamentally an argument that it *doesn't*
need to be in the standard library.

        What? That doesn’t make any sense at all. I’ve written this
proposal to get a very popular and useful type into the standard library
exactly because it’s so popular and useful. It’s so popular and useful, in
fact, that users are running into issues getting all of the third-party
implementations to play well together. I’ll be adding more to the proposal
about the general usefulness of the Result type tomorrow, but it has often
been said on this mailing list that one of the ways a type can become part
of the standard library is by becoming popular in the community.

No no no, that's often been said to be one of the ways to have something
accepted as a _new core library_ (like Foundation, Dispatch, etc.), not as
a part of the standard library. The Swift server API working group, for
example, is working on bootstrapping one or multiple such additional core
libraries.

The standard library is deliberately small, includes facilities essential
for the language itself requiring built-in support, and does *not* have a
goal of supplying all generally useful or sought-after functionality. This
is why, as the Swift Foundation project states, URL is appropriately part
of Foundation and *not* the standard library.

My assertion is that Result has reached that point and so needs
consideration for the benefit of not only all current users of the type,
but the community in general.

I do think you have a good argument to make that error handling is
fundamentally a "language" and not a "library" feature. But you would need
to make the case convincingly by demonstrating how it benefits from
language features. Otherwise, it's a candidate for being part of some core
library but *not* a great candidate for the standard library.

        I could also be confused about what you mean about “built-in”
support, or the types of things you’d think a standard library Result type
should do that isn’t in the proposal. In that case, could you be much more
specific about what additions you think it needs?

Built-in support could include such things as:
* language support for "unwrapping," analogous (though of course not
identical) to `?` and `!`
* compiler magic to reconcile `() throws -> Int` and `() -> Result<Int>` so
that one spelling can be used as an implementation for a protocol
requirement spelled differently, or one spelling can be used in a subclass
overriding a method spelled differently
* any other conveniences necessary to make interconversion between `() ->
Result<T>`, `() throws -> T`, `() -> T?`, and `() -> T!` more fluid from
the caller side or from the library writer side

I am not saying that `Result` definitely needs these things, although I
suspect it could benefit from at least a few such well-designed language
features. In my view, a complete proposal for the _standard library_ would
look into these conveniences, evaluate different possible designs for each,
then discuss which most improve user ergonomics and cohesion of the
language.

The Swift Error Handling Rationale document itself mentions similar as well
as additional Result-related needs:

A function to evaluate an error-producing closure and capture the result

as a Result<T>.

A function to unpack a Result<T> by either returning its value or

propagating the error in the current context.

A futures library that traffics in Result<T> when applicable.
An overload of dispatch_sync which takes an error-producing closure and

propagates an error in the current context.

etc.

In proposing the addition of `Result`, some overarching vision needs to be
articulated that firstly answers the question of: what is or should be part
of the "etc." mentioned above, and how must we design `Result` and any
accompanying language features so that we can eventually get there?

···

On Thu, Nov 2, 2017 at 9:36 PM, Jon Shier <jon@jonshier.com> wrote:

Except | is commutative, so you would except Int | Error to be equivalent
to Error | Int, which isn't the semantic of the Result type.

···

On Fri, Nov 3, 2017 at 4:04 PM, Elia Cereda <eliacereda@gmail.com> wrote:

I'd say that this syntax would be even more coherent with protocol
composition, given that x is effectively an Int *or* an Error:

var x: Int | Error

But aside from the specific syntax, I'm pretty sure it isn't the first
time this request comes up and there were good reasons for rejecting it.

Elia Cereda

Il giorno 03 nov 2017, alle ore 13:10, Benjamin G via swift-evolution < > swift-evolution@swift.org> ha scritto:

Actually i'd even prefer :
var x: Int ?? Error

the spaces makes it more readable, it looks like what you'd do with the ??
operator already, and it seems coherent with the syntax for protocol
composition.

On Fri, Nov 3, 2017 at 12:12 PM, Benjamin G <benjamin.garrigues@gmail.com> > wrote:

Just an idea for the type declaration :

Why not use the same ? as Optional, but with the type of the error behind
:

Such as

var x: Int?Error

Optional Int (Int?) would be seen a special case of Result where the
error type is nil.

The advantage of this syntax is that it would let us specify the type of
the error if we want it.

On Fri, Nov 3, 2017 at 11:45 AM, Nick Keets via swift-evolution < >> swift-evolution@swift.org> wrote:

Right, to me there is not a lot of value in adding Result as it exists
in AlamoFire. We will (eventually) use the Swift Package Manager for things
like this. The value would be in integrating it like Optionals. e.g. (using
a strawman symbol)

    var x: Int‽ = 5
    var y: Int‽ = anErrorValue

    func foo() -> Int‽ { ... }

    if let x = foo() {
        // x is Int
    } else {
        // somehow access the error
    }

    guard let x = foo() else {
        // Again somehow access the error
    }

    func bar() throws -> String { ... }
    let x = try‽ bar() // x is String‽
    let y = x! // y is String

    // Possibly even make it throw? (just using a random symbol again)
    let z = try x¡

On Fri, Nov 3, 2017 at 2:02 AM, Xiaodi Wu via swift-evolution < >>> swift-evolution@swift.org> wrote:

This is clearly a fine addition to the standard library; even Swift's
Error Handling Rationale (https://github.com/apple/swif
t/blob/master/docs/ErrorHandlingRationale.rst) mentions such an
addition

What separates standard library types from other types is that they
have language level support, and the wrapping and unwrapping syntax here
could definitely benefit from it (`.unwrap()`--which should be
`.unwrapped()` incidentally--is so much less elegant in comparison to `?`
and `!` for optionals (not that `Result` should use the exact such syntax
for a distinct operation)). It would be a shame to transpose a third-party
`Result` to the standard library without considering if any such tweaks
would substantially improve ergonomics, interconversion with Optional and
throws, etc.

On Thu, Nov 2, 2017 at 1:08 PM, Jon Shier via swift-evolution < >>>> swift-evolution@swift.org> wrote:

Swift-Evolution:
I’ve written a first draft of a proposal to add Result<T> to the
standard library by directly porting the Result<T> type used in Alamofire
to the standard library. I’d be happy to implement it (type and tests for
free!) if someone could point me to the right place to do so. I’m not
including it directly in this email, since it includes the full
implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/propos
als/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hello,

I wish that the Swift-Evolution discussion around typed / untyped throws would avoid three traps:

- a bloodshed between pro and anti typed-throws
- a pro-typed-throws echo chamber (since untyped throws is the passive status quo, and typed throws the energetic challenger)
- even less average-joe-programmers than ever (on a topic that immensely impacts their everyday job)

Shouldn't the community elect a benevolent dictator that would settle this subject? The final choice has of course to be sensible, but also strong and sharp.

Gwendal

···

Le 9 nov. 2017 à 08:57, Chris Lattner via swift-evolution <swift-evolution@swift.org> a écrit :

On Nov 6, 2017, at 4:13 AM, Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:

  This consideration is further complicated by the possible addition of typed throws in the future. However, the most commonly suggested implementation fo typed throws keeps the ability for throws to be untyped. Additionally, the feature usually allows multiple types to be thrown from a single function. Result<T> can handle all of these scenarios automatically, since it reduces all errors down to Error. A Result<T, E> however, would either lose the ability to encapsulate any function with multiple error types, or otherwise have to wrap those cases in something like AnyError, in additional to having to do so in the untyped case.

As I mentioned up-thread, this proposal isn’t going to go anywhere without a proper discussion of typed throws. There are two possible designs that have strong rationale:

1. Never add typed throws.
2. Add the ability to specify a single type thrown (typically an enum, but could be a struct), which defaults to Error if unspecified.

In contrast, I don’t see any reason to add an arbitrary *list* of thrown types, and I can’t imagine such a design happening. Swift already has ways to specify alternatives (enums) and this would encourage exactly the behavior from APIs that we want to avoid.

This choice between 1/2 needs to be decided before introducing result, because #1 means it should be Result<T>, and #2 means it should be Result<T,E>. If this is important to you, I’d suggest starting a dedicated discussion thread about the topic.

2 Likes

This isn’t an argument against Result, it’s an argument against all error encapsulation in Swift at all. Which is fine for your personal project, but frankly I don’t see it as a bad thing as a language capability. Like any other use of type-inference, the compiler guarantees you can’t use the value of the function directly but must go through the Result value to get it, which serves as the indication of there being an error there. Given the usage of Result in the Swift community, I’m thinking your concerns aren’t shared by the vast majority of Swift users. It’s entirely possible for something to exist in the language and have it not be recommended as the default implementation for something. For example, if/guard let are usually recommended over map or flatMap’ing Optional, but the capability exists because it’s very useful when the recommended pattern breaks down. Result is no different.

Jon

···

On Nov 2, 2017, at 3:30 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Thu, Nov 2, 2017 at 12:21 PM Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:
  The Result type I’ve outlined includes the functions necessary to translate between do try catch and Result. But I fundamentally disagree with your characterization of Swift’s error handling. At most, Swift strives to hide the line between normal functions and error throwing ones, but only until the developer needs access to those errors. If you’re using an API that takes throwing functions and handles the do / catch for you, then sure, you’ll never see the result of those functions as anything more than the values you get from those APIs But anyone writing their own try / catch statements is quite clearly going to see the result/error separation that Result encapsulates. In quite a few cases, passing an encapsulation around rather than having to either mark all functions as throwing in order to propagate an error, or constantly implementing try / catch, results is code that is far easier to read, write, and reason about it.

I would think the opposite is true.

    let value = compute()

Looking at this line of code, I have no idea whether I should expect to handle errors or not. Does "compute" return a Result<> or just the value I want? If we used throwing instead, it's obvious without any other context that an error can occur there:

    let value = try compute()

That's another explicit design choice in Swift, that "try" marks expressions so it's very clear which expressions can throw, rather than just being a block that surrounds an arbitrarily long amount of code. Result<> provides none of that context.

Of course it’s not an absolute solution, which is why the ability to turn a Result into a throwing function exists, but instead a complement to the current error handling in Swift.

Jon

On Nov 2, 2017, at 3:11 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

On Thu, Nov 2, 2017 at 11:58 AM Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:
  You would continue to be free to discourage the usage of Result for whatever you want. For the rest of us, Result isn’t intended to replace throws or do/catch, but provide a way to accomplish things in a much more compact and sometimes natural way. As with any API it could be used stupidly. But frankly, what developers what to do to wrap their errors is up to them.

And it still is, with the Result implementations that are available to third-parties today.

My concerns regarding Result aren't about my personal discouragement of its use, but the *reasons* why I discourage its use. The Swift language very deliberately draws a line between result outcomes that are return values and error outcomes that are thrown, and it implements not only standard library types but also language syntactic sugar to support those.

If someone wants to depend on a third-party Result<> to conflate successful outcomes and error outcomes in their own code, that's absolutely their right. But entry into the standard library has a much higher bar, and what we're talking about here is adding a feature that now would give users two disparate and incompatible ways (without explicit transformations, or other syntactic sugar) of handling errors. That makes me uneasy from the point of view of both an API designer and consumer, and just restating that it's a common pattern and people want it doesn't address those concerns.

Adding Result is just a way of blessing a result/error representation, since it has become a rather common pattern. If you’ve looked at the implementation I showed, you’ll see that there’s far more functionality than just a Result type, including API for converting back and forth from throwing functions, as well as functional transforms. Result is a complement to try do catch, not a replacement.

Jon

On Nov 2, 2017, at 2:48 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

On Thu, Nov 2, 2017 at 11:32 AM Jon Shier <jon@jonshier.com <mailto:jon@jonshier.com>> wrote:
  That’s been an argument against Result for 2 years now. The usefulness of the type, even outside of whatever asynchronous language support the core team comes up with, perhaps this year, perhaps next year, is still very high. Even as something that just wraps throwing functions, or otherwise exists as a local, synchronous value, it’s still very useful as way to encapsulate the value/error pattern.

This is one of the parts that concerns me, actually. The beauty of Swift's error design is that function results denote expected/successful outcomes and thrown errors denote unexpected/erroneous outcomes. Since they are different, each is handled through its own language constructs, and since the language itself supports it (rather than being entirely type-based), you don't have the proliferation of unwrapping boilerplate that you have with Result<>.

In our own code bases, I actively discourage the use of Result<> in that way, because it tries to cram both of those concepts into the expected/successful outcomes slot in the language. For asynchronous APIs that's somewhat unavoidable today, but if that's going to change, I'd rather the language focus on a way that's consistent with other error handling already present in Swift.

Adding an API to the standard library is the core team saying "this is blessed as something around which we support APIs being designed." IMO, I'd prefer it if the language did *not* bless two disparate ways of communicating error outcomes but rather converged on one.

IMO, "things aren't happening fast enough" isn't great motivation for putting something permanently into the standard library or the language without considering the context of other things going on around it. If you're going to propose something that overlaps with asynchronous APIs, it only helps your case if you can discuss how it can integrate—rather than collide—with those efforts.

That pattern will likely never go away. Additionally, having the Result type in the standard library removes a source of conflict between all other Result implementations, which are becoming more common.

On Nov 2, 2017, at 2:26 PM, Tony Allevato <tony.allevato@gmail.com <mailto:tony.allevato@gmail.com>> wrote:

Given that the Swift team is currently working on laying the groundwork for asynchronous APIs using an async/await model, which would presumably tie the throwing cases more naturally into the language than what is possible using completion-closures today, are we sure that this wouldn't duplicate any efforts there or be made obsolete through other means?

In other words, while Result<> can be a very useful foundational component on its own, I think any proposal for it can't be made in isolation, but very much needs to consider other asynchronous work going on in the language.

On Thu, Nov 2, 2017 at 11:15 AM Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
You don’t lose it, it’s just behind `Error`. You can cast out whatever strong error type you need without having to bind an entire type to it generically. If getting a common error type out happens a lot, I usually add a convenience property to `Error` to do the cast for me. Plus, having to expose an entire new error wrapper is just a non starter for me and doesn’t seem necessary, given how Result is currently used in the community.

Jon

On Nov 2, 2017, at 2:12 PM, Dave DeLong <swift@davedelong.com <mailto:swift@davedelong.com>> wrote:

I think I’d personally rather see this done with a generic error as well, like:

enum GenericResult<T, E: Error> {
  case success(T)
  case failure(E)
}

And a typealias:

typealias Result<T> = GenericResult<T, AnyError>

This would require an “AnyError” type to type-erase a specific Error, but I’ve come across many situations where a strongly-typed error is incredibly useful, and I’d be reluctant to see that thrown away.

Dave

On Nov 2, 2017, at 12:08 PM, Jon Shier via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Swift-Evolution:
  I’ve written a first draft of a proposal to add Result<T> to the standard library by directly porting the Result<T> type used in Alamofire to the standard library. I’d be happy to implement it (type and tests for free!) if someone could point me to the right place to do so. I’m not including it directly in this email, since it includes the full implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/proposals/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

This isn’t an argument against Result, it’s an argument against all error
encapsulation in Swift at all. Which is fine for your personal project, but
frankly I don’t see it as a bad thing as a language capability. Like any
other use of type-inference, the compiler guarantees you can’t use the
value of the function directly but must go through the Result value to get
it, which serves as the indication of there being an error there. Given the
usage of Result in the Swift community, I’m thinking your concerns aren’t
shared by the vast majority of Swift users. It’s entirely possible for
something to exist in the language and have it not be recommended as the
default implementation for something. For example, if/guard let are usually
recommended over map or flatMap’ing Optional, but the capability exists
because it’s very useful when the recommended pattern breaks down. Result
is no different.

Optional<>.flatMap isn't the same, because the *API being consumed* isn't
what's changing—only how one chooses to consume it. It doesn't matter if
you use if-let/guard-let or .flatMap, the type you're dealing with is still
Optional<>. APIs that need something optional use Optional<>, period.

Result<>, on the other hand, opens the door to bifurcating the API space
into those that are throwing and those that are Result-returning, when
they're trying to convey the same information. Sure, there are explicit
functions to transform Result to throwing and vice-versa, but it's still
something that forces certain decisions on API consumers. Code that
consumes thrown errors vs. code that handles Result-return values will look
different and inconsistent depending on which technique the API designer
chose. That's not ideal.

The proposal states the premise that Result<> is commonly desired and
popular and is therefore a good fit for the standard library. Aside from
its popularity, however, the only use case mentioned is asynchronous
APIs—but the proposal doesn't mention any of the ongoing work in that area,
and it doesn't offer any other examples of where Result<> would be clearly
superior enough that warrant it being placed on equal ground with Swift's
native error handling. It would be extremely helpful to flesh that out
more—if there *are* strong use cases for why the language should offer
users the ability to declare two different forms of error-propagating APIs,
the proposal would be helped by showing them.

···

On Thu, Nov 2, 2017 at 12:41 PM Jon Shier <jon@jonshier.com> wrote:

Jon

On Nov 2, 2017, at 3:30 PM, Tony Allevato <tony.allevato@gmail.com> wrote:

On Thu, Nov 2, 2017 at 12:21 PM Jon Shier <jon@jonshier.com> wrote:

The Result type I’ve outlined includes the functions necessary to
translate between do try catch and Result. But I fundamentally disagree
with your characterization of Swift’s error handling. At most, Swift
strives to hide the line between normal functions and error throwing ones,
but only until the developer needs access to those errors. If you’re using
an API that takes throwing functions and handles the do / catch for you,
then sure, you’ll never see the result of those functions as anything more
than the values you get from those APIs But anyone writing their own try /
catch statements is quite clearly going to see the result/error separation
that Result encapsulates. In quite a few cases, passing an encapsulation
around rather than having to either mark all functions as throwing in order
to propagate an error, or constantly implementing try / catch, results is
code that is far easier to read, write, and reason about it.

I would think the opposite is true.

    let value = compute()

Looking at this line of code, I have no idea whether I should expect to
handle errors or not. Does "compute" return a Result<> or just the value I
want? If we used throwing instead, it's obvious without any other context
that an error can occur there:

    let value = try compute()

That's another explicit design choice in Swift, that "try" marks
expressions so it's very clear which expressions can throw, rather than
just being a block that surrounds an arbitrarily long amount of code.
Result<> provides none of that context.

Of course it’s not an absolute solution, which is why the ability to turn
a Result into a throwing function exists, but instead a complement to the
current error handling in Swift.

Jon

On Nov 2, 2017, at 3:11 PM, Tony Allevato <tony.allevato@gmail.com> >> wrote:

On Thu, Nov 2, 2017 at 11:58 AM Jon Shier <jon@jonshier.com> wrote:

You would continue to be free to discourage the usage of Result for
whatever you want. For the rest of us, Result isn’t intended to replace
throws or do/catch, but provide a way to accomplish things in a much more
compact and sometimes natural way. As with any API it could be used
stupidly. But frankly, what developers what to do to wrap their errors is
up to them.

And it still is, with the Result implementations that are available to
third-parties today.

My concerns regarding Result aren't about my personal discouragement of
its use, but the *reasons* why I discourage its use. The Swift language
very deliberately draws a line between result outcomes that are return
values and error outcomes that are thrown, and it implements not only
standard library types but also language syntactic sugar to support those.

If someone wants to depend on a third-party Result<> to conflate
successful outcomes and error outcomes in their own code, that's absolutely
their right. But entry into the standard library has a much higher bar, and
what we're talking about here is adding a feature that now would give users
two disparate and incompatible ways (without explicit transformations, or
other syntactic sugar) of handling errors. That makes me uneasy from the
point of view of both an API designer and consumer, and just restating that
it's a common pattern and people want it doesn't address those concerns.

Adding Result is just a way of blessing a result/error representation,
since it has become a rather common pattern. If you’ve looked at the
implementation I showed, you’ll see that there’s far more functionality
than just a Result type, including API for converting back and forth from
throwing functions, as well as functional transforms. Result is a
complement to try do catch, not a replacement.

Jon

On Nov 2, 2017, at 2:48 PM, Tony Allevato <tony.allevato@gmail.com> >>> wrote:

On Thu, Nov 2, 2017 at 11:32 AM Jon Shier <jon@jonshier.com> wrote:

That’s been an argument against Result for 2 years now. The usefulness
of the type, even outside of whatever asynchronous language support the
core team comes up with, perhaps this year, perhaps next year, is still
very high. Even as something that just wraps throwing functions, or
otherwise exists as a local, synchronous value, it’s still very useful as
way to encapsulate the value/error pattern.

This is one of the parts that concerns me, actually. The beauty of
Swift's error design is that function results denote expected/successful
outcomes and thrown errors denote unexpected/erroneous outcomes. Since they
are different, each is handled through its own language constructs, and
since the language itself supports it (rather than being entirely
type-based), you don't have the proliferation of unwrapping boilerplate
that you have with Result<>.

In our own code bases, I actively discourage the use of Result<> in that
way, because it tries to cram both of those concepts into the
expected/successful outcomes slot in the language. For asynchronous APIs
that's somewhat unavoidable today, but if that's going to change, I'd
rather the language focus on a way that's consistent with other error
handling already present in Swift.

Adding an API to the standard library is the core team saying "this is
blessed as something around which we support APIs being designed." IMO, I'd
prefer it if the language did *not* bless two disparate ways of
communicating error outcomes but rather converged on one.

IMO, "things aren't happening fast enough" isn't great motivation for
putting something permanently into the standard library or the language
without considering the context of other things going on around it. If
you're going to propose something that overlaps with asynchronous APIs, it
only helps your case if you can discuss how it can integrate—rather than
collide—with those efforts.

That pattern will likely never go away. Additionally, having the Result
type in the standard library removes a source of conflict between all other
Result implementations, which are becoming more common.

On Nov 2, 2017, at 2:26 PM, Tony Allevato <tony.allevato@gmail.com> >>>> wrote:

Given that the Swift team is currently working on laying the groundwork
for asynchronous APIs using an async/await model, which would presumably
tie the throwing cases more naturally into the language than what is
possible using completion-closures today, are we sure that this wouldn't
duplicate any efforts there or be made obsolete through other means?

In other words, while Result<> can be a very useful foundational
component on its own, I think any proposal for it can't be made in
isolation, but very much needs to consider other asynchronous work going on
in the language.

On Thu, Nov 2, 2017 at 11:15 AM Jon Shier via swift-evolution < >>>> swift-evolution@swift.org> wrote:

You don’t lose it, it’s just behind `Error`. You can cast out whatever
strong error type you need without having to bind an entire type to it
generically. If getting a common error type out happens a lot, I usually
add a convenience property to `Error` to do the cast for me. Plus, having
to expose an entire new error wrapper is just a non starter for me and
doesn’t seem necessary, given how Result is currently used in the community.

Jon

On Nov 2, 2017, at 2:12 PM, Dave DeLong <swift@davedelong.com> wrote:

I think I’d personally rather see this done with a generic error as
well, like:

enum GenericResult<T, E: Error> {
case success(T)
case failure(E)
}

And a typealias:

typealias Result<T> = GenericResult<T, AnyError>

This would require an “AnyError” type to type-erase a specific Error,
but I’ve come across many situations where a strongly-typed error is *incredibly
*useful, and I’d be reluctant to see that thrown away.

Dave

On Nov 2, 2017, at 12:08 PM, Jon Shier via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

Swift-Evolution:
I’ve written a first draft of a proposal to add Result<T> to the
standard library by directly porting the Result<T> type used in Alamofire
to the standard library. I’d be happy to implement it (type and tests for
free!) if someone could point me to the right place to do so. I’m not
including it directly in this email, since it includes the full
implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/proposals/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Where are the guiding documents for swift's concurrency support? I have an
unrelated idea for the language that I think would be a sizable win for
concurrency. (I won't cross post it here.)

RE Error syntax:
Syntactic sugar that makes it easier to ignore errors is a miss-step, I
think. Errors differ from optionals in how you handle the un-happy path.

With optionals: if the data isn't there you either O1) abort, or O2) follow
an alternative, acceptable path.
With errors: if the data isn't there you either E1) log-and-abort or E2)
present the error, either directly or via propagation.

That is to say – with errors the programmer has a greater responsibly to
honor the _content_ of the error, not just its presence.
To merely ignore the error (which is possible to do with the `try?` syntax,
is almost always an anti-pattern.

TL;DR. Error handling bares a greater responsibly than optional handling.
Responsible code is safer code.

I support the introduction of a `Result` type into the language (perhaps as
a core-lib like Dispatch), because it follows the principle of
responsibility.

RE Async syntax:
In like manner, I don't think it's a good idea to make the _call site_ of
asynchronous functions the same as the call site of synchronous functions.
If this were the case it would be very easy to confuse asynchronous
functions as synchronous and accidentally make your own function
asynchronous.

If there is any change to the language to add syntactic wrappers around
async methods, I think they should be lexically demarcated just like
throwing functions are. For example:

try someThrowingFunction()

async someAsyncFunction() // the natural call signature here would have
been someAsyncFunction(callback: {} )

Just like `try` is not for the compiler, but is a flag for the programmer
to pay attention, so – I think – there should be a similar flag that causes
the programmer to pay attention to async functions.

EPILOG :
But the above example actually takes us directly into the topic of
promises/futures. (What is _actually_ returned if we call an asynchronous
function with synchronous syntax?)
I have lot of thoughts on promises, safely-implemented async functions, and
the general practice of concurrent programming in swift. But maybe this
isn't the best thread for that broader discussion. ???

Thoughts?

···

On Fri, Nov 3, 2017 at 12:16 PM, Benjamin G via swift-evolution < swift-evolution@swift.org> wrote:

Except | is commutative, so you would except Int | Error to be equivalent
to Error | Int, which isn't the semantic of the Result type.

On Fri, Nov 3, 2017 at 4:04 PM, Elia Cereda <eliacereda@gmail.com> wrote:

I'd say that this syntax would be even more coherent with protocol
composition, given that x is effectively an Int *or* an Error:

var x: Int | Error

But aside from the specific syntax, I'm pretty sure it isn't the first
time this request comes up and there were good reasons for rejecting it.

Elia Cereda

Il giorno 03 nov 2017, alle ore 13:10, Benjamin G via swift-evolution < >> swift-evolution@swift.org> ha scritto:

Actually i'd even prefer :
var x: Int ?? Error

the spaces makes it more readable, it looks like what you'd do with the
?? operator already, and it seems coherent with the syntax for protocol
composition.

On Fri, Nov 3, 2017 at 12:12 PM, Benjamin G <benjamin.garrigues@gmail.com >> > wrote:

Just an idea for the type declaration :

Why not use the same ? as Optional, but with the type of the error
behind :

Such as

var x: Int?Error

Optional Int (Int?) would be seen a special case of Result where the
error type is nil.

The advantage of this syntax is that it would let us specify the type of
the error if we want it.

On Fri, Nov 3, 2017 at 11:45 AM, Nick Keets via swift-evolution < >>> swift-evolution@swift.org> wrote:

Right, to me there is not a lot of value in adding Result as it exists
in AlamoFire. We will (eventually) use the Swift Package Manager for things
like this. The value would be in integrating it like Optionals. e.g. (using
a strawman symbol)

    var x: Int‽ = 5
    var y: Int‽ = anErrorValue

    func foo() -> Int‽ { ... }

    if let x = foo() {
        // x is Int
    } else {
        // somehow access the error
    }

    guard let x = foo() else {
        // Again somehow access the error
    }

    func bar() throws -> String { ... }
    let x = try‽ bar() // x is String‽
    let y = x! // y is String

    // Possibly even make it throw? (just using a random symbol again)
    let z = try x¡

On Fri, Nov 3, 2017 at 2:02 AM, Xiaodi Wu via swift-evolution < >>>> swift-evolution@swift.org> wrote:

This is clearly a fine addition to the standard library; even Swift's
Error Handling Rationale (https://github.com/apple/swif
t/blob/master/docs/ErrorHandlingRationale.rst) mentions such an
addition

What separates standard library types from other types is that they
have language level support, and the wrapping and unwrapping syntax here
could definitely benefit from it (`.unwrap()`--which should be
`.unwrapped()` incidentally--is so much less elegant in comparison to `?`
and `!` for optionals (not that `Result` should use the exact such syntax
for a distinct operation)). It would be a shame to transpose a third-party
`Result` to the standard library without considering if any such tweaks
would substantially improve ergonomics, interconversion with Optional and
throws, etc.

On Thu, Nov 2, 2017 at 1:08 PM, Jon Shier via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

Swift-Evolution:
I’ve written a first draft of a proposal to add Result<T> to the
standard library by directly porting the Result<T> type used in Alamofire
to the standard library. I’d be happy to implement it (type and tests for
free!) if someone could point me to the right place to do so. I’m not
including it directly in this email, since it includes the full
implementation and is therefore quite long. (Discourse, please!)

https://github.com/jshier/swift-evolution/blob/master/propos
als/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

These are MHOs, but if you want to talk concurrency, you should start a new thread:

-Chris

···

On Nov 3, 2017, at 1:43 PM, Alex Lynch via swift-evolution <swift-evolution@swift.org> wrote:

Where are the guiding documents for swift's concurrency support? I have an unrelated idea for the language that I think would be a sizable win for concurrency. (I won't cross post it here.)

This is different from the normal evolution process—especially now that we have an implementation requirement—but I think I'd like to see the pro-typed-throws side put together a proposal, and then we can bring it up for a review and decide whether we *eventually* (even if it's not important enough for Swift 5) want to have typed throws or not. People have been asking for this feature for years, nobody seems to think it has insurmountable technical obstacles, and it's now holding up other decisions, and yet it's never been a high enough priority for us to just make the decision. We should change that.

If it's rejected in review, we can design `Result` and everything else on the assumption that we'll never have it. If it's accepted, we'll know where the roadmap is going there, too. I might not even demand a detailed technical design—just a clear outline of what will and won't be possible (e.g. multiple error types or not?), some discussion of the use cases and drawbacks, and a chance to make a binding(-ish) decision on the question.

···

On Nov 9, 2017, at 3:16 AM, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

I wish that the Swift-Evolution discussion around typed / untyped throws would avoid three traps:

- a bloodshed between pro and anti typed-throws
- a pro-typed-throws echo chamber (since untyped throws is the passive status quo, and typed throws the energetic challenger)
- even less average-joe-programmers than ever (on a topic that immensely impacts their everyday job)

Shouldn't the community elect a benevolent dictator that would settle this subject? The final choice has of course to be sensible, but also strong and sharp.

--
Brent Royal-Gordon
Architechies

Your point about async and the current work on that side of the language
seems convincing to me, however I'd say that Result could also be seing as
an improvement over Optional for handling what the
"ErrorHandlingRationale.rst" document calls "Simple Domain Errors" (so, not
requiring throw), but for which you also want to provide the reason for
returning nil (either for logging, or for any other reason).

ie : there seems to be an area not well covered by the language for
type-safe , self documenting, failable function calls. Throwing being a bit
"too much" and not self documenting enough (since you can't specify what
type of error the function is throwing), and optional is great if there's
only one possible reason for failing, but insufficient otherwise.

PS: once again, i haven't read the whole conversation at the time this list
discussed error handling, so sorry if all of this has already been
discussed..

···

On Thu, Nov 2, 2017 at 9:44 PM, Tony Allevato via swift-evolution < swift-evolution@swift.org> wrote:

On Thu, Nov 2, 2017 at 12:41 PM Jon Shier <jon@jonshier.com> wrote:

This isn’t an argument against Result, it’s an argument against all error
encapsulation in Swift at all. Which is fine for your personal project, but
frankly I don’t see it as a bad thing as a language capability. Like any
other use of type-inference, the compiler guarantees you can’t use the
value of the function directly but must go through the Result value to get
it, which serves as the indication of there being an error there. Given the
usage of Result in the Swift community, I’m thinking your concerns aren’t
shared by the vast majority of Swift users. It’s entirely possible for
something to exist in the language and have it not be recommended as the
default implementation for something. For example, if/guard let are usually
recommended over map or flatMap’ing Optional, but the capability exists
because it’s very useful when the recommended pattern breaks down. Result
is no different.

Optional<>.flatMap isn't the same, because the *API being consumed* isn't
what's changing—only how one chooses to consume it. It doesn't matter if
you use if-let/guard-let or .flatMap, the type you're dealing with is still
Optional<>. APIs that need something optional use Optional<>, period.

Result<>, on the other hand, opens the door to bifurcating the API space
into those that are throwing and those that are Result-returning, when
they're trying to convey the same information. Sure, there are explicit
functions to transform Result to throwing and vice-versa, but it's still
something that forces certain decisions on API consumers. Code that
consumes thrown errors vs. code that handles Result-return values will look
different and inconsistent depending on which technique the API designer
chose. That's not ideal.

The proposal states the premise that Result<> is commonly desired and
popular and is therefore a good fit for the standard library. Aside from
its popularity, however, the only use case mentioned is asynchronous
APIs—but the proposal doesn't mention any of the ongoing work in that area,
and it doesn't offer any other examples of where Result<> would be clearly
superior enough that warrant it being placed on equal ground with Swift's
native error handling. It would be extremely helpful to flesh that out
more—if there *are* strong use cases for why the language should offer
users the ability to declare two different forms of error-propagating APIs,
the proposal would be helped by showing them.

Jon

On Nov 2, 2017, at 3:30 PM, Tony Allevato <tony.allevato@gmail.com> >> wrote:

On Thu, Nov 2, 2017 at 12:21 PM Jon Shier <jon@jonshier.com> wrote:

The Result type I’ve outlined includes the functions necessary to
translate between do try catch and Result. But I fundamentally disagree
with your characterization of Swift’s error handling. At most, Swift
strives to hide the line between normal functions and error throwing ones,
but only until the developer needs access to those errors. If you’re using
an API that takes throwing functions and handles the do / catch for you,
then sure, you’ll never see the result of those functions as anything more
than the values you get from those APIs But anyone writing their own try /
catch statements is quite clearly going to see the result/error separation
that Result encapsulates. In quite a few cases, passing an encapsulation
around rather than having to either mark all functions as throwing in order
to propagate an error, or constantly implementing try / catch, results is
code that is far easier to read, write, and reason about it.

I would think the opposite is true.

    let value = compute()

Looking at this line of code, I have no idea whether I should expect to
handle errors or not. Does "compute" return a Result<> or just the value I
want? If we used throwing instead, it's obvious without any other context
that an error can occur there:

    let value = try compute()

That's another explicit design choice in Swift, that "try" marks
expressions so it's very clear which expressions can throw, rather than
just being a block that surrounds an arbitrarily long amount of code.
Result<> provides none of that context.

Of course it’s not an absolute solution, which is why the ability to
turn a Result into a throwing function exists, but instead a complement to
the current error handling in Swift.

Jon

On Nov 2, 2017, at 3:11 PM, Tony Allevato <tony.allevato@gmail.com> >>> wrote:

On Thu, Nov 2, 2017 at 11:58 AM Jon Shier <jon@jonshier.com> wrote:

You would continue to be free to discourage the usage of Result for
whatever you want. For the rest of us, Result isn’t intended to replace
throws or do/catch, but provide a way to accomplish things in a much more
compact and sometimes natural way. As with any API it could be used
stupidly. But frankly, what developers what to do to wrap their errors is
up to them.

And it still is, with the Result implementations that are available to
third-parties today.

My concerns regarding Result aren't about my personal discouragement of
its use, but the *reasons* why I discourage its use. The Swift language
very deliberately draws a line between result outcomes that are return
values and error outcomes that are thrown, and it implements not only
standard library types but also language syntactic sugar to support those.

If someone wants to depend on a third-party Result<> to conflate
successful outcomes and error outcomes in their own code, that's absolutely
their right. But entry into the standard library has a much higher bar, and
what we're talking about here is adding a feature that now would give users
two disparate and incompatible ways (without explicit transformations, or
other syntactic sugar) of handling errors. That makes me uneasy from the
point of view of both an API designer and consumer, and just restating that
it's a common pattern and people want it doesn't address those concerns.

Adding Result is just a way of blessing a result/error representation,
since it has become a rather common pattern. If you’ve looked at the
implementation I showed, you’ll see that there’s far more functionality
than just a Result type, including API for converting back and forth from
throwing functions, as well as functional transforms. Result is a
complement to try do catch, not a replacement.

Jon

On Nov 2, 2017, at 2:48 PM, Tony Allevato <tony.allevato@gmail.com> >>>> wrote:

On Thu, Nov 2, 2017 at 11:32 AM Jon Shier <jon@jonshier.com> wrote:

That’s been an argument against Result for 2 years now. The usefulness
of the type, even outside of whatever asynchronous language support the
core team comes up with, perhaps this year, perhaps next year, is still
very high. Even as something that just wraps throwing functions, or
otherwise exists as a local, synchronous value, it’s still very useful as
way to encapsulate the value/error pattern.

This is one of the parts that concerns me, actually. The beauty of
Swift's error design is that function results denote expected/successful
outcomes and thrown errors denote unexpected/erroneous outcomes. Since they
are different, each is handled through its own language constructs, and
since the language itself supports it (rather than being entirely
type-based), you don't have the proliferation of unwrapping boilerplate
that you have with Result<>.

In our own code bases, I actively discourage the use of Result<> in
that way, because it tries to cram both of those concepts into the
expected/successful outcomes slot in the language. For asynchronous APIs
that's somewhat unavoidable today, but if that's going to change, I'd
rather the language focus on a way that's consistent with other error
handling already present in Swift.

Adding an API to the standard library is the core team saying "this is
blessed as something around which we support APIs being designed." IMO, I'd
prefer it if the language did *not* bless two disparate ways of
communicating error outcomes but rather converged on one.

IMO, "things aren't happening fast enough" isn't great motivation for
putting something permanently into the standard library or the language
without considering the context of other things going on around it. If
you're going to propose something that overlaps with asynchronous APIs, it
only helps your case if you can discuss how it can integrate—rather than
collide—with those efforts.

That pattern will likely never go away. Additionally, having the
Result type in the standard library removes a source of conflict between
all other Result implementations, which are becoming more common.

On Nov 2, 2017, at 2:26 PM, Tony Allevato <tony.allevato@gmail.com> >>>>> wrote:

Given that the Swift team is currently working on laying the
groundwork for asynchronous APIs using an async/await model, which would
presumably tie the throwing cases more naturally into the language than
what is possible using completion-closures today, are we sure that this
wouldn't duplicate any efforts there or be made obsolete through other
means?

In other words, while Result<> can be a very useful foundational
component on its own, I think any proposal for it can't be made in
isolation, but very much needs to consider other asynchronous work going on
in the language.

On Thu, Nov 2, 2017 at 11:15 AM Jon Shier via swift-evolution < >>>>> swift-evolution@swift.org> wrote:

You don’t lose it, it’s just behind `Error`. You can cast out
whatever strong error type you need without having to bind an entire type
to it generically. If getting a common error type out happens a lot, I
usually add a convenience property to `Error` to do the cast for me. Plus,
having to expose an entire new error wrapper is just a non starter for me
and doesn’t seem necessary, given how Result is currently used in the
community.

Jon

On Nov 2, 2017, at 2:12 PM, Dave DeLong <swift@davedelong.com> wrote:

I think I’d personally rather see this done with a generic error as
well, like:

enum GenericResult<T, E: Error> {
case success(T)
case failure(E)
}

And a typealias:

typealias Result<T> = GenericResult<T, AnyError>

This would require an “AnyError” type to type-erase a specific Error,
but I’ve come across many situations where a strongly-typed error is *incredibly
*useful, and I’d be reluctant to see that thrown away.

Dave

On Nov 2, 2017, at 12:08 PM, Jon Shier via swift-evolution < >>>>>> swift-evolution@swift.org> wrote:

Swift-Evolution:
I’ve written a first draft of a proposal to add Result<T> to the
standard library by directly porting the Result<T> type used in Alamofire
to the standard library. I’d be happy to implement it (type and tests for
free!) if someone could point me to the right place to do so. I’m not
including it directly in this email, since it includes the full
implementation and is therefore quite long. (Discourse, please!)

GitHub - jshier/swift-evolution: This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.
proposals/0187-add-result-to-the-standard-library.md

Thanks,

Jon Shier

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hello there,

I was reading the proposal for Result<T> and wanted to suggest an addition to the Result type that we use in our frameworks.

It consist of a nested Block type as follows:

extension Result {

    /// Repository methods can simplify their signatures by using this typealias.
    public typealias Block = (Result<T>) -> Void

}

This becomes handy when defining methods that take in a callback block which returns a Result type.

Callback without Result.Block:

func myAsyncMethod(callback: @escaping (Result<Foo>) -> Void) {
    // Do something...
    callback(.success("foobar"))
} 

Callback with Result.Block:

func myAsyncMethod(callback: @escaping Result<Foo>.Block) {
    // Do something...
    callback(.success("foobar"))
} 

It does not seem like much, but is specially useful when blocks are optional.

Optional callback without Result.Block:

func myAsyncMethod(callback: ((Result<Foo>) -> Void)?) {
    // Do something...
    callback?(.success("foobar"))
} 

Optional callback with Result.Block:

func myAsyncMethod(callback: Result<Foo>.Block?) {
    // Do something...
    callback?(.success("foobar"))
} 

Hope you find it useful enough to be included.

Regards,
Eneko

1 Like

While the concept is interesting, I don't like the name. It is not descriptive and confusing, as Swift closures are not Block (Block is an Obj-C term).
They are not even compatible with block, unless explicitly requested (by applying custom calling convention).

Agreed about the naming, which is of little consequence. We use this too.

I think the bigger issue here is Result<T> had a lot of pushback. I think the fact it’s used so ubiquitously in other libraries makes it valuable enough to include in a shared swift “Extended Library” ala Boost but at this stage I’m not sure the Core Team have much interest in implementing something like this that they won’t use in the Standard Library...

I personally think it is time to standardize Result, and I believe that several other core team members feel the same way. The major problem with doing so is that is blocked by an unresolved disagreement (in both the core team and community): whether Swift should support typed errors.

If we add typed throws, then I believe that the design is clear: throws would allow at most one type to be specified, and if none is specified, Error is the default:

func foo() throws {
func bar() throws Error {  // same as above
func baz() throws MyEnum {   // ok

While many people will argue for a list of possible thrown things, there are lots of reasons not to do that, not least of which is that you can represent such a list of options with an enum.

Whether we add this or not ends up impacting Result. In a world without typed throws, we should define result as Result. If we have typed throws, we should have Result<NormalResultType, TheErrorType>.

On this core disagreement, there are lots of reasonable arguments on both sides, but it would be healthy to start hashing those out (in its own thread). We don't need to actually implement typed throws to get result, we just need a decision about whether we should implement it one day. Not making that decision continues the sad state of affairs where we don't have a standardized Result type.

-Chris

20 Likes

(Making sure typed throws goes into a different, probably ridiculously long thread - assuming in any examples here that it is just Result)

IMO the thing which cements Result in the standard library is language and compiler support for Result comparable to Optional. Most of the features in the Swift namespace are there because they are features that involve language syntax (optional, literal types, error) and/or because they are needed for language-level C/ObjC interoperability (array, string, C vararg, unsafe memory access, data). Even stream I/O isn't in the standard library.

What would I think compiler support look like? A random assortment of potential features would be:

  • Automatic promotion of a T to Result<T>.
  • terse try syntax for unwrapping result to a value or error, possible custom syntax for case expressions.
  • Optional chaining style usage to deal with Result holding an error
  • Functional equivalence/compatibility of styles. A developer should not have to use wrappers to have a single function definition or closure usable as (for instance):
    • func foo<X,T>(arg:X, callback:(T?, Error?)->())
    • func foo<X,T>(arg:X, callback:(Result<T>)->())
    • async func foo<X,T>(arg:X) throws -> T
  • Possible rethrows-style allowances, where generic algorithms and the compiler can preserve the fact that a type will not represent an error.
  • Special compiler optimizations of Result (such as avoiding creation of intermediate type instances when unnecessary, or removing unnecessary code knowing that a particular code path cannot return a failure, under the premise the optimizer wouldn't already do these).
3 Likes

I'd like to circle back to the notion of unwrappable that you designed way back if we're going to have a formal Result type.

I don't think Result needs to be tied to error handling. It's useful to be able to interoperate with throws, but a two-argument Result would be useful for clients regardless, and maximally future-proof. We could constrain throws interop APIs to E == Error for now, and reserve the right to generalize them to E: Error if the language ever supports typed throws.

1 Like

Rust has a Try-trait that may be worth looking at, if I'm understanding correctly what you are talking about.

@dwaite

Automatic promotion of a T to Result<T>.

And also promotion of an E to a .failure(E), right?

terse try syntax for unwrapping result to a value or error, possible custom syntax for case expressions.

I'm not sure exactly what you want, but the proposed Result has unwrap(), so you can do let value = try result.unwrap()`. This could be integrated with other unwrapping proposals as well.

Optional chaining style usage to deal with Result holding an error

I can't imaging what this would look like, can you post an example?

Functional equivalence/compatibility of styles. A developer should not have to use wrappers to have a single function definition or closure usable as (for instance):

func foo<X,T>(arg:X, callback:(T?, Error?)->())
func foo<X,T>(arg:X, callback:(Result<T>)->())
async func foo<X,T>(arg:X) throws -> T

I'm not sure automatic conversions from callback: (T?, E?) -> Void should be supported in Swift, but I can imagine some sort of Objective-C markup to allow such a conversion to happen when APIs are exposed to Swift. However, I certainly envision Result being able to be used as the manual propagation of whatever async features we get in the future, similar to how it can be thought of as the manual propagation of throws right now.

Possible rethrows-style allowances, where generic algorithms and the compiler can preserve the fact that a type will not represent an error.

We can start with whatever the compiler already does with its invariant analysis and go from there. I'd guess Optional has similar cases.

Special compiler optimizations of Result (such as avoiding creation of intermediate type instances when unnecessary, or removing unnecessary code knowing that a particular code path cannot return a failure, under the premise the optimizer wouldn’t already do these).

Sure. This may be similar to other optimizations around enums.

These are all nice things, but they're all additive, as far as I can see, and shouldn't impact the initial introduction of the type.