[swift-evolution] [Pre-Draft] Nil-coalescing and errors

Yuta Koshizawa koher at koherent.org
Thu Apr 7 08:23:09 CDT 2016


Of course we can implement a library with `throws` by changing those
properties to methods. However we can think about similar cases.

```
do {
    let foo: Foo = try foo(
        a: Int(aString) ??? Error(),
        b: Int(bString) ??? Error(),
        c: Int(cString) ??? Error()
    )
} catch _ {
    // Error handling
}
```

`Dictionary` also returns optionals. We have already had various
functions/methods which returns optionals.

-- Yuta


2016-04-07 21:34 GMT+09:00 Thorsten Seitz via swift-evolution
<swift-evolution at swift.org>:
> Why not have throwing variants of the `string` and `int` methods (in
> addition to those returning an optional)?
>
> Then you could simply write:
>
> ```
> // Decodes a JSON with SwiftyJSON
> do {
>    let person: Person = try Person(
>       firstName: json["firstName"].string,
>       lastName: json["lastName"].string,
>       age: json["age"].int)
> } catch _ {
>     // Error handling
> }
> ```
>
> -Thorsten
>
>
> Am 07. April 2016 um 14:01 schrieb Yuta Koshizawa via swift-evolution
> <swift-evolution at swift.org>:
>
> I'd like to see some real-world examples of this before we did anything with
> it.
>
>
> The following is my real-world example.
>
> ```
> // Decodes a JSON with SwiftyJSON
> do {
> let person: Person = try Person(
> firstName: json["firstName"].string ??? Error(),
> lastName: json["lastName"].string ??? Error(),
> age: json["age"].int ??? Error()
> )
> } catch _ {
> // Error handling
> }
> ```
>
> With `guard`, we have to repeat the parameter names meaninglessly.
>
> ```
> do {
> guard let firstName = json["firstName"].string else { throw Error() }
> guard let lastName = json["lastName"].string else { throw Error() }
> guard let age = json["age"].string else { throw Error() }
> let person: Person = Person(firstName: firstName, lastName:
> lastName, age: age)
> } catch _ {
> // Error handling
> }
> ```
>
> `guard` is a statement. `???` makes an expression. Expressions are
> useful when we want to pass their return values as arguments directly.
> I think `???` is valuable to get an unwrapped value or throw an error
> as an expression.
>
> -- Yuta
>
> 2016-04-07 2:45 GMT+09:00 Jordan Rose via swift-evolution
> <swift-evolution at swift.org>:
>
> I think I'm with Sean on this one. Optionals and throwing don't have enough
>
> to do with each other to actually come up with a specific operator or method
>
> for this. I can't help but see this as two ideas glued together:
>
>
> - "By this point in my execution I need a non-optional value, otherwise
>
> ______"
>
> - "_____ happened, therefore execution has failed and I should throw an
>
> error"
>
>
> …and I'm not sure these ideas coincide enough to be worth gluing together.
>
> There are a lot of other ways to get a non-optional value out of an optional
>
> ('??', '!', and 'guard let' with some other action), and there are a lot of
>
> other ways to fail besides an optional being nil (status code came back as
>
> error, unexpected data, connection timeout).
>
>
> I'd like to see some real-world examples of this before we did anything with
>
> it.
>
>
> Jordan
>
>
>
> On Apr 6, 2016, at 8:00, Sean Heber via swift-evolution
>
> <swift-evolution at swift.org> wrote:
>
>
> Interesting, but I’m unsure if all of it is significantly better than just
>
> using the guard that is effectively inside of the operator/func that is
>
> being proposed:
>
>
> guard let value = Int("NotANumber") else { throw
>
> InitializerError.invalidString }
>
>
> It is only a couple of characters longer and already works (it’s what I use
>
> currently). If guard allowed for a special single-expression variation so
>
> that you didn’t need to specify the ugly braces or something, it’d look
>
> prettier and be nice for a lot of other situations, too:
>
>
> guard let value = Int("NotANumber") else: throw
>
> InitializerError.invalidString
>
> guard someVal < 10 else: return false
>
> guard mustBeTrue() else: return
>
> // etc
>
>
> Not to derail this, but I sort of want this ability anywhere as a shorthand
>
> for a single-expression block.
>
>
> if something < 42: doThing()
>
> for a in list: print(a)
>
>
> But I imagine that’ll never fly. :P
>
>
> l8r
>
> Sean
>
>
>
>
> On Apr 6, 2016, at 9:46 AM, Erica Sadun via swift-evolution
>
> <swift-evolution at swift.org> wrote:
>
>
> Pyry Jahkola and I have been plugging away on the following which is
>
> preliminary enough not to qualify as an actual draft. He prefers the Mike
>
> Ash approach. I prefer the operator approach. So we have not actually
>
> settled on which one we would actually propose despite how I've written this
>
> up.
>
>
> I'm putting this out there to try to gain a consensus on:
>
>
> * Would this be a viable proposal?
>
> * If so, which of the options would work best within Swift's design and
>
> philosophy
>
>
> Thanks for your feedback.
>
>
> -- Erica
>
> Introduction
>
>
> Swift's try? keyword transforms error-throwing operations into optional
>
> values. We propose adding an error-throwing nil-coalescing operator to the
>
> Swift standard library. This operator will coerce optional results into
>
> Swift's error-handling system.
>
>
> This proposal was discussed on the Swift Evolution list in the name thread.
>
>
> Motivation
>
>
> Any decision to expand Swift's set of standard operators should be taken
>
> thoughtfully and judiciously. Moving unaudited or deliberately
>
> non-error-handling nil-returning methods and failable initializers into
>
> Swift's error system should be a common enough use case to justify
>
> introducing a new operator.
>
>
> Detail Design
>
>
> We propose adding a new operator that works along the following lines:
>
>
> infix operator ??? {}
>
>
> func ???<T>(lhs: T?, @autoclosure error: () -> ErrorType) throws -> T {
>
> guard case let value? = lhs else { throw error() }
>
> return value
>
> }
>
>
> The use-case would look like this:
>
>
> do {
>
> let error = Error(reason: "Invalid string passed to Integer initializer")
>
> let value = try Int("NotANumber") ??? InitializerError.invalidString
>
> print("Value", value)
>
> } catch { print(error) }
>
>
> Note
>
>
> SE-0047 (warn unused result by default) and SE-0049 (move autoclosure) both
>
> affect many of the snippets in this proposal
>
>
> Disadvantages to this approach:
>
>
> • It consumes a new operator, which developers must be trained to use
>
> • Unlike many other operators and specifically ??, this cannot be chained.
>
> There's no equivalent to a ?? b ?? c ?? dor a ?? (b ?? (c ?? d)).
>
> Alternatives Considered
>
>
> Extending Optional
>
>
> The MikeAsh approach extends Optional to add an orThrow(ErrorType) method
>
>
> extension Optional {
>
> func orThrow(@autoclosure error: () -> ErrorType) throws -> Wrapped {
>
> guard case let value? = self else { throw error() }
>
> return value
>
> }
>
> }
>
>
> Usage looks like this:
>
>
> do {
>
> let value = try Int("NotANumber")
>
> .orThrow(InitializerError.invalidString)
>
> print("Value", value)
>
> } catch { print(error) }
>
>
> An alternative version of this call looks like this: optionalValue.or(throw:
>
> error). I am not a fan of using a verb as a first statement label.
>
>
> Disadvantages:
>
>
> • Wordier than the operator, verging on claustrophobic, even using Swift's
>
> newline dot continuation.
>
> • Reading the code can be confusing. This requires chaining rather than
>
> separating error throwing into a clear separate component.
>
> Advantages:
>
>
> • No new operator, which maintains Swift operator parsimony and avoids the
>
> introduction and training issues associated with new operators.
>
> • Implicit Optional promotion cannot take place. You avoid mistaken usage
>
> like nonOptional ??? error and nonOptional ?? raise(error).
>
> • As a StdLib method, autocompletion support is baked in.
>
> Introducing a StdLib implementation of raise(ErrorType)
>
>
> Swift could introduce a raise(ErrorType) -> T global function:
>
>
> func raise<T>(error: ErrorType) throws -> T { throw error }
>
>
> do {
>
> let value = try Int("NotANumber") ??
>
> raise(InitializerError.invalidString)
>
> print("Value", value)
>
> } catch { print(error) }
>
>
> This is less than ideal:
>
>
> • This approach is similar to using && as an if-true condition where an
>
> operator is abused for its side-effects.
>
> • It is wordier than the operator approach.
>
> • The error raising function promises to return a type but never will, which
>
> seems hackish.
>
> Overriding ??
>
>
> We also considered overriding ?? to accept an error as a RHS argument. This
>
> introduces a new way to interpret ?? as meaning, "throw this error instead
>
> of substituting this value".
>
>
> func ??<T>(lhs: T?, @autoclosure error: () -> ErrorType) throws -> T {
>
> guard case let value? = lhs else { throw error() }
>
> return value
>
> }
>
>
> Usage:
>
>
> let value = try Int("NotANumber") ?? Error(reason: "Invalid string passed to
>
> Integer initializer")
>
>
> This approach overloads the semantics as well as the syntax of the
>
> coalescing operator. Instead of falling back to a RHS value, it raises the
>
> RHS error. The code remains simple and readable although the developer must
>
> take care to clarify through comments and naming which version of the
>
> operator is being used.
>
>
> • While using try in the ?? statement signals that a throwing call is in
>
> use, it is insufficient (especially when used in a throwing scope) to
>
> distinguish between the normal coalescing and new error-throwing behaviors.
>
> • Error types need not use the word "Error" in their construction or use.
>
> For example try value ?? e may not be immediately clear as an error-throwing
>
> intent.
>
> • Overloading ?? dilutes the impact and meaning of the original operator
>
> intent.
>
> Future Directions
>
>
> We briefly considered something along the lines of perl's die as an
>
> alternative to raise using fatalError.
>
>
> Acknowledgements
>
>
> Thanks Mike Ash, Jido, Dave Delong
>
> _______________________________________________
>
> swift-evolution mailing list
>
> swift-evolution at swift.org
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
>
> swift-evolution mailing list
>
> swift-evolution at swift.org
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
>
> _______________________________________________
>
> swift-evolution mailing list
>
> swift-evolution at swift.org
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>


More information about the swift-evolution mailing list