My proposal is to remove the need for a concrete error to be used in rethrowing functions when the optional try? variant is used to call it.
This is a very small suggestion - I came across it as a small inconvenience while developing yesterday. I was using reduce(into:_:) to construct a dictionary, like so:
MyObject.dictValue() here is a throwing function. Since I only wanted a final value, and only wanted to return nil when the parsing failed, I could efficiently escape the reduce function and return nil immediately.
Since I was using try? and returning nil on failure, I did not care what error was thrown and I did not need to handle it. The compiler forced me to add a concrete error case here.
The updated code would look much the same, except with the concrete error removed:
This is my first post on the Swift Evolution forums and so my apologies if I pitched this incorrectly - please let me know if there are ways I can improve on it for next time.
Additionally, I can imagine that this could be very complex to implement since it essentially requires the compiler to reason about two separate functions. If you think it is far too complex (for a marginal benefit), I'd be interested to hear more about why this is.
The compiler would have to prove that no caller inspects the error. While this is feasible for non-public methods, it wouldn't be possible to make that assumption for cross-module callers.
I have often needed something similar. What I usually do is something like this:
I wonder if there is – or should be – some “default” or “standard” error which can be thrown in such a situation, or if the error is catched locally:
do {
// do something ...
if exceptionalCase throw Error.default
// continue doing something ...
} catch {
// handle exceptional case
}
You have to make up some domain and code, otherwise you'll get a
let err = NSError()
// -[NSError init] called; this results in an invalid NSError instance.
// It will raise an exception in a future release. Please call
// errorWithDomain:code:userInfo: or initWithDomain:code:userInfo:.
(Tested with Xcode 11.3.1 in a command line application.)
NSError is a good workaround, but what if there was a way to do this that didn't rely on Cocoa APIs?
Martin's original suggestion can't be implemented as a static member on the Error protocol, but could be done as a global function. The fileprivate modifier and an opaque return type would remove the issue of having a new free-floating Standard Library type:
fileprivate enum _AnonymousError: Error {
case `default`
}
public func anonymousError() -> some Error {
_AnonymousError.default
}
I tested it in Xcode and this can be called from outside due to the opaque return type of Error.
So, the function can throw both from your lookup method and from explicitly throwing after testing for nil, right? It seems that the second way is there only because you want to force errors in the second way to share handling from the first way, right? I think the best solution is not to force one way of error handling into another and use normal processing for each style.
Here, that means dumping reduce for a conventional for-in loop. Guard on each extraction call to return nil upon failure. Surround the loop with a do-catch to return nil on throw.
Having throw fall back to a default error seems like a bad idea for general code, and adding a whole complex analysis to allow it in narrow cases seems awfully specialized.