Can we get a "try?" as a more lenient "try!"?

I'm impressed by the structure imposed by the new do-catch construct.

I also appreciate that the Swift team gave us try! to handle cases where we are sure that errors won't occur.

Now one case that keeps bugging me when refactoring to Swift 2.0 code is I have to write something like this:

let fileManager = NSFileManager.defaultManager()
do {
   try fileManager.removeFileAtPath(cachePath1)
} catch _ { }

do {
   try fileManager.removeFileAtPath(cachePath2)
} catch _ { }

do {
   try fileManager.removeFileAtPath(cachePath3)
} catch _ { }


I just want to delete those paths if possible, but I don't care if they fail; they may have not existed in the first place and if they did, the system will clean the Caches sooner or later.

If I write this as

do {
   try fileManager.removeFileAtPath(cachePath1)
   try fileManager.removeFileAtPath(cachePath2)
   try fileManager.removeFileAtPath(cachePath3)
} catch _ { }

then if cachePath1 fails, 2 and 3 will never be executed. And while we don't care if the deletes fail, not trying at all just feels wrong. What if each method I call have a side effect that I need to trigger?


On the other hand,

try! fileManager.removeFileAtPath(cachePath1)
try! fileManager.removeFileAtPath(cachePath2)
try! fileManager.removeFileAtPath(cachePath3)

will crash my app if any of the deletes fail. Definitely not what I want.


Now I don't know if someone else had the same idea before, but what if we have a try? as well to counterpart the try!? We can write

try? fileManager.removeFileAtPath(cachePath1)

which ignores any thrown errors, and more importantly, does not trap on error.


For methods that return an object, Swift 2.0 already bridges Objective-C methods of the pattern

- (SomeObject *)doSomethingAndReturnError:(NSError **)error;


to

func doSomething() throws -> SomeObject


such that doing

let object: SomeObject = try! doSomething()

returns a non-optional SomeObject instance.


We can then make "try?" return an optional:

let object = try? doSomething() // object will be bound to SomeObject?


For void methods, returning a Void? is fine, or we can borrow the bridging rules from Objective-C and just return a Bool instead.

if try? fileManager.removeFileAtPath(cachePath1) {
   println("file deleted!")
}


Is this idea too crazy or am I missing something that will break this kind of syntax?



Update:

I hope the discussion does not digress into a debate wether to handle file-system errors or not; I just used removeFileAtPath as ONE of the many use cases we encountered this problem. CharlesS pointed out another good use case: methods that were primarily used to check for a state, such as NSURL.checkResourceIsReachable()

Accepted Reply

Looks like "try?" has been added to Beta 6:


http://adcdownload.apple.com/Developer_Tools/Xcode_7_beta_6/Xcode_7_beta_6_Release_Notes.pdf

Replies

I don't see the point in your argument because as I have mentioned in this particular case, neither result is relevant to my code. The discussion here isn't wether we should handle errors or not, its how to to write code elegantly when we are already decided we do not need to handle them.


Please don't take this sentimentally. Just because we're requesting this try? feature doesn't mean we want to ignore all errors and use it everywhere; just in particular places where it doesn't make sense to do otherwise. As Wallacy has explained well below, Apple introduced the new do-catch syntax in the recent WWDC by first acknowledging different levels of error-handling. We just want to be able to demote certain errors as "Trivial errors".

I'm afraid you are misunderstanding the use of try!. It's not meant to be used to "abdicate responsibility", it's used (in the documentation's own words) for "cases in which you know a throwing function or method won’t, in fact, throw an error at run time".

Indeed. The other thing is that since it's now so much less painful to report errors than it used to be, it's much more tempting to use throws in just about any call that can fail, where you used to just return an optional (I've recently converted a whole bunch of methods in my own code). This much more liberal use of error reporting, however, can cause headaches for the caller without a try? syntax to opt into the older behavior.

This was such a good expression of why a try? keyword would be useful, it really deserves a reply from an Apple engineer (unless they're all on holiday).

If they don't do it, people will create their own little hacks. We're talking about error code, people - sometimes reality will tell us that we misjudged the importance of some error, or in general handled it incorrectly. By bringing all of these constructs in to the language, we will at least have consistency when we come to debug this stuff, including being able to turn on logging or trapping for all hitherto-ignored errors.

Well, it looks like they have dealt with this simply by converting checkResourceIsReachable back into a non-throwing method; now it's once again checkResourceIsReachableAndReturnError. Still no fix for use cases like the OP's, unfortunately.

The one issue with 'try?' that I see is that the '!' family indicates code may be making unsafe assumptions in the case of code review. A try? may actually be an unsafe assumption - that we can ignore the error value returned from a method; that the error won't cascade further down the line making debugging hellish. It's just not an unsafe assumption that immediately results in the app asserting/crashing.


There are cases where you should be able to safely ignore an error, but I fear 'try?' makes it way too easy to ignore errors that the developer should actually be acting on. Some sort of standard library 'ignorethrow' @noescape function may be more appropriate

"Unsafe assumptions" seem to be the theme for the "?" marker already. Take as? for example. You may assume that it evaluates to `nil` if it fails downcasts, but it may actually be upcast failures or that the original value is `nil`.

The thing is, most methods in the iOS SDK that report errors have a way to show success or failure without having to look at the errors themselves. Either they return true/false, or an object/nil. try? just let us "ignore errors" in such a way that we don't have to create a do-catch scope when all we need is to know the true/false or object/nil result. In other words, yes you are explicitly ignoring errors but you can still handle success and failure.

Looks like "try?" has been added to Beta 6:


http://adcdownload.apple.com/Developer_Tools/Xcode_7_beta_6/Xcode_7_beta_6_Release_Notes.pdf

I'm glad they added this; certainly saves a few lines of code and scope nesting.

One thing that beta 6 added that goes well with "try?" is the warning of unused return values. for example, writing

try? fileManager.removeFileAtPath(cachePath1)


will result in the warning "Result of 'try?' is unused". This prevents accidental bugs and you can still explicitly indicate if you did this on purpose:

_ = try? fileManager.removeFileAtPath(cachePath1)

Blech. This is as clunky as the original work-arounds if you don't care about the return value.


I filed a radar on this, asking that discarding a Void? return not generate a warning (i.e., it should be treated just like Void for warning purposes). Does this seem like a reasonable enhancement?

I don't know if removing warnings for "Void?" is a good tradeoff. Most of the Objective-C methods that return BOOL and an optional error pointer becomes "throws -> Void" in swift, so ignoring "Void?" is just as good as ignoring "AnyObject?".


For me, the current "try?" lets me write a one-liner without forcing me to push a new scope so ignoring warnings with a "_ = try? ..." is a good enough compromise.

Right. The purpose of 'try?' is not to allow you to ignore errors; it's to treat all errors the same way, inline. If you want to ignore the error, you have to say so explicitly. (And if there should never be an error, use 'try!'.)