[swift-evolution] [Proposal Idea] catching functions for composable and cps error handling

Matthew Johnson matthew at anandabits.com
Thu Dec 17 09:05:19 CST 2015


Yes of course you can do these things.  And you could use a Result type for error handling before Swift 2.  The problem is that these are ad-hoc approaches and everyone will do it slightly differently.

Error handling is important enough to justify a language feature that aids correctness and establishes a common practice.  This idea may or not be the right solution.  Either way I do believe Swift will be better off with a language level solution.


> On Dec 17, 2015, at 12:13 AM, Thorsten Seitz <tseitz42 at icloud.com> wrote:
> 
> What about just writing the following (see below)? To enable the same behaviour we would have to add non-local return which would be nice for other reasons. 
> 
> func handler<T>(block: () -> T) rethrows {
>         do {
>                 let result = try block()
>                 return result
>         } catch VendingMachineError .InvalidSelection {
>                 // some common code handling InvalidSelection
>         } catch VendingMachineError.OutOfStock {
>                // some common code handling OutOfStock
>                // handle some other cases as well
>         }
> }
> 
> func doSomething() {
>         do {
>                 handler {
>                         try someThrowingFunction()
>                 }
>         } catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
>                 // special case handled here
>         }
> }
> 
> For covering your third case using a handler function with an argument, you could add an argument to the func handler.
> For chaining handler functions you could introduce an operator for composing handler functions.
> 
> -Thorsten
> 
> Am 17.12.2015 um 06:27 schrieb Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
> 
>> I came up with an interesting idea that might make a nice enhancement to Swift’s current error handling.  I’m curious to see whether others think it is worth pursuing or not.
>> 
>> There are two motivating examples for this.  It could provide the obvious way to flow errors through code written in continuation passing style (whether asynchronous or not).  It could also provide a way to abstract over common error handling code.
>> 
>> The basic idea is to allow catching functions that would be a complement to throwing functions.  Catching functions would have one or more catch blocks at the top level and could also accept arguments:
>> 
>> func handler(int i: Int, str: String) catches {
>>   // i and str are in scope
>> } catch VendingMachineError.InvalidSelection {
>>   // i and str are not in scope
>> } catch VendingMachineError.OutOfStock {
>>   // i and str are not in scope
>> }
>> 
>> 
>> The function could be called as normal: 
>> handler(int: 1, str: “”)  
>> 
>> In this case the body would be executed.
>> 
>> The function could also be called with a “catch” clause: 
>> handler(catch VendingMachineError.InvalidSelection)
>> 
>> In this case the top level catch clauses would be evaluated as if they part of a do-catch statement in which an error was thrown.
>> 
>> Note that there is no colon after the `catch` in the function call.  This would avoid conflicting with a potential argument named catch and would also call attention to the fact that it is not a normal argument.  I don’t think 	`throw` would be appropriate here as that could be ambiguous if the function containing the call to `handler` was a throwing function and also because an error would not be thrown up the stack, but rather caught directly by `handler`.
>> 
>> It may be worthwhile to consider requiring a catching function to handle all errors if it wishes to return a value so that it is able to return a value when catching an error no matter what the error is.  
>> 
>> Alternatively, (and maybe more interesting) since the compiler knows at the call site whether the function was provided regular arguments or an error to catch these two cases could be handled independently, with the body returning a value and the result of a “catching” call returning a value indicating whether the error was handled or not (or something similar).
>> 
>> 
>> Here is how this would look when it is applied to the motivating examples.  
>> 
>> First is a cps example using a catching closure.
>> 
>> // @exhaustive requires the catching function to catch all errors
>> func cps(then: Int -> () @exhaustive catches) {
>>   if (checkSomeState) {
>>     then(42)
>>   } else {
>>     then(catch VendingMachineError.InvalidSelection)
>>   }
>> }
>> 
>> cps() { i: Int in
>>   // do some work using i
>> } catch VendingMachineError.InvalidSelection {
>>   // handle the error, i is not in scope
>> } catch VendingMachineError.OutOfStock {
>>   // handle the error, i is not in scope
>> } catch {
>>   // handle all other errors
>> }
>> 
>> Second is an example showing how this could be used to abstract over error handling logic:
>> 
>> func handler() catches {
>> catch VendingMachineError .InvalidSelection {
>>   // some common code handling InvalidSelection
>> catch VendingMachineError.OutOfStock {
>>   // some common code handling OutOfStock
>> // handle some other cases as well
>> }
>> 
>> func doSomething() {
>> 
>>   do {
>> 
>>     try someThrowingFunction()
>> 
>>   } catch handler { // compiler inserts call: handler(catch errorThatWasThrown)
>> 
>>     // not sure if a body would make sense here
>>     // if it does it would only be executed when handler actually handled the error
>> 
>>   // we only proceed to the next case if handler did not handle the error
>>   } catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
>> 
>>   // we can provide arguments to the error handling logic by calling a function that returns a catching closure
>>   } catch someFunctionReturningAClosureThatCatches(arg: someValueDeterminingHowToHandleErrors)
>>   }
>> 
>> }
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151217/03310253/attachment.html>


More information about the swift-evolution mailing list