[swift-evolution] do try catch?

Liam Butler-Lawrence liamdunn at me.com
Mon Dec 7 11:40:15 CST 2015


Hey Kametrixom,

Thanks for the feedback! I agree that the proposed syntax needs improvement.

> guard let unwrapped = optional, result = try test() else {
>     
> } catch SomeError.SomeCase {
>     
> } catch {
>     
> }


I like this example, but it seems rather specific. Are you just using the  guard let unwrapped = optional to show how guard let and the catch block could be used together, or suggesting that guard let should be required for this functionality? If the latter, how would we handle throwing functions that don’t return a value?

I’m fine with removing the enclosing catch { } from my original suggestion; perhaps something like this? My only concern with this is that it’s slightly confusing to have the first catch X on the same line as the try call.

> let result = try test() catch SomeError.SomeCase {
>     
> } catch {
>     
> }


Also, regarding the new syntax being redundant:

> let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
> 
> do {
>     let documentsDirectoryURL = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
>     
>     let persistentStoreFileName = "CoreDataStore.sqlite"
>     let persistentStoreURL = documentsDirectoryURL.URLByAppendingPathComponent(persistentStoreFileName)
>     
>     try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: persistentStoreURL, options: nil)
>     
> } catch MyError.Case1 {
>     
> } catch MyError.Case2 {
>     
> } catch MyError.Case3 {
>     
> } catch AnotherError.Case1 {
>     
> } catch AnotherError.Case2 {
>     
> } catch AnotherError.Case3 {
>     
> } catch {
>     
> }

Thanks for pointing out this possibility. However, this code makes it even harder to decipher where MyError will be thrown from. How about AnotherError– which function threw that? No way to know. In certain cases that may not be an issue, but what if the error handling code presented a UI error? I might want to know more than just what the error is, but the context in which it was generated as well. Putting all the catch blocks at the end directly impacts readability.

As an extension of this issue, suppose that both throwing functions threw the same kind of error. If I want to use a different response based on which function call actually threw the error, I simply can’t do that using this method.

Look forward to hearing your thoughts. Thanks again!

Liam

> On Dec 7, 2015, at 10:49 AM, Kametrixom Tikara <kametrixom at icloud.com> wrote:
> 
> Hi Liam
> 
> I really like that idea, maybe the syntax needs a bit of adjustment. What do you think about this:
> 
> guard let unwrapped = optional, result = try test() else {
>     
> } catch SomeError.SomeCase {
>     
> } catch {
>     
> }
> 
> However I don’t really know if this is needed since it’s possible to call as many throwing functions as you want in a do-catch block:
> 
> let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
> 
> do {
>     let documentsDirectoryURL = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
>     
>     let persistentStoreFileName = "CoreDataStore.sqlite"
>     let persistentStoreURL = documentsDirectoryURL.URLByAppendingPathComponent(persistentStoreFileName)
>     
>     try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: persistentStoreURL, options: nil)
>     
> } catch MyError.Case1 {
>     
> } catch MyError.Case2 {
>     
> } catch MyError.Case3 {
>     
> } catch AnotherError.Case1 {
>     
> } catch AnotherError.Case2 {
>     
> } catch AnotherError.Case3 {
>     
> } catch {
>     
> }
> 
> Which makes the need of such a feature redundant. I really like the idea and would love this to be added :)
> 
>> On 07 Dec 2015, at 16:24, Liam Butler-Lawrence via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>> 
>> I completely agree Wei. Using try as a keyword placed before every throwing function is one of the greatest strengths of Swift’s error-handling model.
>> 
>> I do have a new proposal regarding do/try/catch (if I should post this in a new thread, please let me know):
>> 
>> ————
>> 
>> The problem:
>> 
>> In Swift 1.0, the only way to conditionally unwrap an optional was with if let. This ended up causing the “pyramid of doom” so often that in Swift 2.0, guard let was introduced.
>> 
>> The same problem is present with do/try/catch. Consider this code snippet:
>> 
>> let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
>> 
>> do {
>>     
>>     let documentsDirectoryURL = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
>>     
>>     let persistentStoreFileName = "CoreDataStore.sqlite"
>>     let persistentStoreURL = documentsDirectoryURL.URLByAppendingPathComponent(persistentStoreFileName)
>>     
>>     do {
>>         try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: persistentStoreURL, options: nil)
>>     }
>>     catch AnotherError.Case1 {
>>         
>>     }
>>     catch AnotherError.Case2 {
>>         
>>     }
>>     catch AnotherError.Case3 {
>>         
>>     }
>>     catch {
>>         
>>     }
>> 
>> }
>> catch MyError.Case1 {
>>     
>> }
>> catch MyError.Case2 {
>>     
>> }
>> catch MyError.Case3 {
>>     
>> }
>> catch {
>>     
>> }
>> 
>> First, yes, I know that URLForDirectory() doesn’t throw MyError. I’m illustrating the common pattern using actual function calls.
>> 
>> I see two problems with the above code:
>> 
>> 1. The standard execution of code (if no errors are thrown) gets more and more nested with every call to a throwing function, causing a pyramid of doom. There’s only two levels in this example, but it could easily be four or five.
>> 
>> 2. The first try NSFileManager.default… is no less than 20 lines away from its catch statement. Again, with more try statements, this number only increases, making the code very unreadable in my opinion.
>> 
>> 
>> A possible solution:
>> 
>> Allow a catch clause to be used directly after any function that throws. This clause includes all the catch x {} catch y {} error-handling code. Just like the else clause in a guard let, this catch clause has to return or otherwise break normal execution. Because of this, the overarching do {} catch {} blocks are no longer needed, and normal execution continues without continually increasing code indentation.
>> 
>> This is how the above code snippet might look if this solution was implemented:
>> 
>> let coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
>> 
>> let documentsDirectoryURL = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false) catch {
>>     catch MyError.Case1 {
>>         
>>     }
>>     catch MyError.Case2 {
>>         
>>     }
>>     catch MyError.Case3 {
>>         
>>     }
>>     catch {
>>         
>>     }
>> }
>> let persistentStoreFileName = "CoreDataStore.sqlite"
>> let persistentStoreURL = documentsDirectoryURL.URLByAppendingPathComponent(persistentStoreFileName)
>> 
>> try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: persistentStoreURL, options: nil) catch {
>>     catch AnotherError.Case1 {
>>         
>>     }
>>     catch AnotherError.Case2 {
>>         
>>     }
>>     catch AnotherError.Case3 {
>>         
>>     }
>>     catch {
>>         
>>     }
>> }
>> 
>> The syntax of the new catch block could certainly be improved on (it might also benefit from typed throwing as discussed in another thread). However, even imperfect, this code is much more readable than what’s currently required, as one can instantly tell which try statement goes with which catch statements. In addition, the the pyramid of doom is gone, and the resulting code is reminiscent of a function with many guard lets.
>> 
>> If you have any feedback or know of potential issues that I’m not aware of, I look forward to hearing from you. Thanks so much!
>> 
>> Liam
>> 
>>> On Dec 7, 2015, at 9:36 AM, 王巍 via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> The first (current) choice is better in fact.
>>> 
>>> With try closure out of the scope, you could not tell exactly which statement would throw. 
>>> In your example, only `makeSandwich` would throw, so there is no need to mark `eatASandwich` with a `try`.
>>> 
>>> Current design follows minimal conception very well, you could get to know that only the `makeASandwich` would throw an error, which reduce a lot of noise when reading this code.
>>> 
>>> 
>>> Best regards.
>>> ---
>>> Sincerely, 
>>> Wei Wang (王巍, @onevcat)
>>> im.onevcat.com <http://im.onevcat.com/>
>>>> 在 2015年12月7日,下午11:30,Don Arnel via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> 写道:
>>>> 
>>>> Is there a reason this format was adopted for error handling:
>>>> 
>>>> do {
>>>>     try makeASandwich()
>>>>     eatASandwich()
>>>> } catch Error.OutOfCleanDishes {
>>>>     washDishes()
>>>> } catch Error.MissingIngredients(let ingredients) {
>>>>     buyGroceries(ingredients)
>>>> }
>>>> 
>>>> rather than this format:
>>>> 
>>>> try {
>>>>     makeASandwich()
>>>>     eatASandwich()
>>>> } catch Error.OutOfCleanDishes {
>>>>     washDishes()
>>>> } catch Error.MissingIngredients(let ingredients) {
>>>>     buyGroceries(ingredients)
>>>> }
>>>> 
>>>> The second format is much more intuitive, and reads easier IMO.
>>>> 
>>>> 
>>>> _______________________________________________
>>>> 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>
>>> 
>>> _______________________________________________
>>> 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>
>> 
>>  _______________________________________________
>> 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/20151207/6be88e5e/attachment.html>


More information about the swift-evolution mailing list