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
> <[email protected]> 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
>> <[email protected]> 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
>> [email protected]
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> [email protected]
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution