[swift-evolution] Proposal: Typed throws
David Owens II
david at owensd.io
Fri Dec 4 14:54:56 CST 2015
> On Dec 4, 2015, at 11:54 AM, John McCall <rjmccall at apple.com> wrote:
>
>> On Dec 4, 2015, at 11:36 AM, David Owens II <david at owensd.io> wrote:
>> For the most part, I feel that typed errors are more of a response to how we need to handle errors within Swift today.
>>
>> var vendingMachine = VendingMachine()
>> vendingMachine.coinsDeposited = 8
>>
>> do {
>> try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
>> } catch VendingMachineError.InvalidSelection {
>> print("Invalid Selection.")
>> } catch VendingMachineError.OutOfStock {
>> print("Out of Stock.")
>> } catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
>> print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
>> } catch { fatalError("this is always needed…”) }
>
> But this is printing. Of course you should be able to generically display an error, but you don’t need static typing for that. Also, I certainly hope you are not actually repeating all this stuff at every catch site.
This was a sample from the Swift docs: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508. I added the missing final `catch` that is required with Swift today.
The point being, the error enum is fully defined but we still need the final `catch` at the end because there is no type information on the `throws` annotation for `buyFavoriteSnack`. So unlike the case when dealing with enums and switch-statements, we lose all compile-time information about coverage of error states when we could know them. The current implementation of `throws` is the only (as far as I can tell) place in Swift that turns a compile-time validation problem into a run-time validation problem.
That’s my concern.
Instead, if we could annotate throws, we could move this into a compile-time validation.
enum VendingMachineError: ErrorType {
case InvalidSelection
case InsufficientFunds(coinsNeeded: Int)
case OutOfStock
}
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws VendingMachineError {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
This allows the compiler to validate the only error information leaving this call site is a `VendingMachineError` and it allows all callers to ensure that they are indeed handling the all of the errors for `VendingMachineError`.
To me, that is a very practical and pragmatic problem and is fundamentally no different than the treatment of exhaustive switch cases needed when dealing with other enum values.
Instead, if I turned the `buyFavoriteSnack` into this:
enum Error<ErrorType> {
case Ok,
case Error(ErrorType)
}
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) -> Error<VendingMachineError>
I then get to leverage the compiler to check that I’m indeed handling all of the VendingMachineErrors.
Yes, I do realize that multiple error types would complicate that, but honestly, I’d rather than the limitation that only a single error type can be propagated up the callstack than to have the ability to have no typed error information at all.
Of course, I can do this myself, but the language is going to win so I still have to deal with how `throws` is treated within the language as a whole.
-David
More information about the swift-evolution
mailing list