[swift-evolution] throws as returning a Result

Joe Groff jgroff at apple.com
Tue Mar 15 12:03:59 CDT 2016


> On Mar 15, 2016, at 6:39 AM, Yuta Koshizawa <koher at koherent.org> wrote:
> 
> 2016-03-15 2:23 GMT+09:00 Joe Groff <jgroff at apple.com>:
>> 
>> Yeah, we extensively discussed adding a Result type internally, but
>> ultimately couldn't justify it. The only real use case we could see in the
>> wild was for threading errors through CPS-inversion-style abstractions like
>> async promises, something we hope to provide proper language support for.
>> More generally, expressing effects as monadic values is a pretty awful
>> abstraction; aside from polluting the Internet with an endless deluge of
>> unhelpful tutorials, they also don't compose cleanly, they impose nesting
>> where is desired—you have to pick between Result<Async<T>> and
>> Async<Result<T>>, or build ResultT<AsyncT<Identity>><T> out of monad
>> transformers—and they don't do the natural thing when used with other
>> higher-order abstractions—if you're mapping a `throws` function over a
>> collection, you probably want to propagate that error like `rethrows` does,
>> not end up with a collection of Result<T>.
> 
> Yes, I know the pain of nested monads and I don't want to encourage
> monadic error handling with awful nests.
> 
> To tell the truth, I ultimately desire to unify `Optional`s, `throws`
> and `Result`s.
> 
> We have already had `Optional`s which can be used in a monadic way. To
> prevent excessive monadic handling, I think we need Automatic
> Propagation for `Optional`s.
> 
> ```
> // Automatic Propagation for `Optional`s
> let a: Int? = ...
> let b: Int? = ...
> 
> do {
>  let sum: Int = (try a) + (try b)
>  ...
> } catch { // if `a` and/or `b` are `nil`
>  ...
> }
> ```
> 
> Although "Error Handling Rational and Proposal" says `Optional`s
> should be used for simple domain errors and are suitable for Manual
> Propagation, I think Automatic Propagation is also useful for
> `Optional`s. We get `nil` not only as errors but also as empty values.
> Our codes are full of `Optional`s. Handling them manually costs a lot.
> So I think it is good to have Automatic Propagation for `Optional`s.
> 
> However it is confusing to mix `Optional`s and `throws` functions with
> the same keyword `try`. So I think something like `typealias
> Optional<T> = Result<T, NilError>`, which could be identical in a
> binary form to current `Optional` with `struct NilError: ErrorType
> {}`, and unified `throws` and `Result`s would be better. Then we would
> have only `Result`s, but it could be used as `Optional`s and `throws`.
> 
> Although `Result`s might make it possible to abuse monadic error
> handling, problems of abuses are also true for other language
> features: e.g. `(Float, Float)` as `Vector2` instead of `struct
> Vector2 { ... }` for tuples. Even if we keep `Optional`s and `throws`
> separated, `Optional`s can be handled monadically and we need to
> encourage people how and when to use them to prevent abuses. I think
> language features cannot prevent abuses, and it is a role of coding
> guidelines.
> 
> So I think it is good to unify `Optional`s, `throws` and `Result`s.
> But because it seemed too radical, I proposed the part of it at first:
> `throws -> Foo` as a syntactic sugar of `-> Result<Foo>`.

I agree, it's nice to be able to open optionals and avoid direct monadic manipulations too. Optionals can be lifted and extracted from a `throws` body fairly easily already, much like a `Result`:

enum Nil: ErrorType { case Nil }
extension Optional {
  func getOrThrow() throws -> Wrapped {
    if let x = self { return x }
    throw Nil.Nil 
  }
}
func doOrNil<T, U>(f: (T) throws -> U, x: T) -> U? {
  do {
    return try f(x)
  } catch {
    return nil
  }
}

The latter is provided by the language as `try?` already. If we had typed `throws`, it would be nice to be able to express `throws Nil` more precisely, of course.

-Joe

> 
>> I'd rather see us adopt an
>> extensible algebraic effects system, something like http://www.eff-lang.org,
>> which provides a framework for `throws`, `async` and other control flow
>> effects to be cleanly composed and abstracted over. I see `throws` as the
>> first seed of that.
> 
> Thank you for the information. Because I am not familiar with Eff, I
> will check it. If it composes multiple abstractions well, it must be
> great!
> 
> -- Yuta

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160315/9c755671/attachment.html>


More information about the swift-evolution mailing list