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