I’m inclined to agree with this; the guard statement is fairly clear, though a 
slightly cleaner construct for throwing would be nice.
That said, it isn’t mutually exclusive with the MikeAsh alternative provided, 
which could still be a nice addition for those that prefer it (though it’s also 
a fairly easy one to add yourself through extension).

Coincidentally, the example in the operator part of the proposal illustrates 
part of why I argued against keeping failable initialisers as a type of error 
handling as it can be easy to result in odd cases when nil is used 
alternatively as an error condition and simply a “no result” case. But that’s 
another topic really.

> On 6 Apr 2016, at 16:00, Sean Heber via swift-evolution 
> <swift-evolution@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@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

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

Reply via email to