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@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@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

Reply via email to