[swift-evolution] Idea: Extend "guard" to try-statements, with a catch block

Erica Sadun erica at ericasadun.com
Wed Mar 23 11:52:23 CDT 2016


The error handler doesn't have to be co-linear or even defined in the same call:

/// consists of filename, line number, error tuple
public typealias CommonErrorHandlerType = (String, Int, ErrorType) -> Void

/// Default error handler prints context and error
public let defaultCommonErrorHandler: CommonErrorHandlerType = {
    filePath, lineNumber, error in
    let trimmedFileName: String = (filePath as NSString).lastPathComponent
    print("Error \(trimmedFileName):\(lineNumber) \(error)")
}

If you're doing much more than printing or adding a line-or-two extra then 
I don't think you should be using guard, you should be applying do-catch at the call site
or using try (not try? or try!) and forwarding the error handling.

-- E


> On Mar 23, 2016, at 8:05 AM, Maximilian Hünenberger <m.huenenberger at me.com> wrote:
> 
> Your "attempt" function addresses this  issue if someone only wants to print the error:
> 
> guard let x = attempt({ try throwingFunction(y) }) else {
>          return
> }
> // prints "Error \(trimmedFileName):\(lineNumber) \(error)"
> 
> In contrast to
> 
> guard let x = try? throwingFunction(y) catch {
>         // you have to make your own print
>         return
> }
> 
> ---------------------
> 
> However if you want to handle the error before you exit the scope it is quite inconvenient:
> 
> guard let x = attempt({ _, _, error in /* handle error */ }, { try throwingFunction(y) } ) else {
>         return
> }
> 
> In contrast to:
> 
> guard let x = try? throwingFunction(y) catch {
>         // handle error
>         return
> }
> 
> - Maximilian
> 
> Am 14.03.2016 um 18:00 schrieb Erica Sadun <erica at ericasadun.com <mailto:erica at ericasadun.com>>:
> 
>>> On Mar 14, 2016, at 10:20 AM, Maximilian Hünenberger via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> 
>>> How about only allowing "guard try? catch":
>>> 
>>>     guard let x = try? foo() catch { ... }
>>> 
>>> This would address both concerns of Chris:
>>> 
>>> • "This is inconsistent with what we currently have, because “if let” and “guard let” match against an optional and succeed iff the optional is present."
>>> 
>>> • "This shouldn’t be tied to the presence of try, because it already means something (that the enclosed expression can throw).  This:
>>>         guard let x = try foo() …
>>> Already means “call foo, if it throws, propagate the error.  If not, test the returned optional”."
>>> 
>>> 
>>> With "try?" it is clear that "foo()" doesn't throw an error in the guard expression and guard matches against an optional. This makes it unambiguous to "guard try else" which throws in this case.
>>> 
>>> Kind regards
>>> - Maximilian
>> 
>> 
>> 
>> I'm not a fan of the notion of guard/catch. However, it occurs to me that my "attempt" code may address this issue.
>> I've recently updated it to take an arbitrary error handler, which if omitted, simply prints the error. Otherwise it acts like try?
>> or if you set crashOnError, like try!. It works with guard.
>> 
>> -- E
>> 
>> github: https://github.com/erica/SwiftUtility/blob/master/Sources/CoreError.swift <https://github.com/erica/SwiftUtility/blob/master/Sources/CoreError.swift>
>> 
>> public typealias CommonErrorHandlerType = (String, Int, ErrorType) -> Void
>> 
>> /// Replacement for `try?` that introduces an error handler
>> /// The default handler prints an error before returning nil
>> ///
>> /// - Parameter file: source file, derived from `__FILE__` context literal
>> /// - Parameter line: source line, derived from `__LINE__` context literal
>> /// - Parameter crashOnError: defaults to false. When set to true
>> ///   will raise a fatal error, emulating try! instead of try?
>> /// - Parameter errorHandler: processes the error, returns nil
>> ///
>> /// ```swift
>> /// attempt {
>> ///   let mgr = NSFileManager.defaultManager()
>> ///   try mgr.createDirectoryAtPath(
>> ///     "/Users/notarealuser",
>> ///     withIntermediateDirectories: true,
>> ///     attributes: nil)
>> /// }
>> /// ```
>> ///
>> public func attempt<T>(
>>     file fileName: String = __FILE__,
>>     line lineNumber: Int = __LINE__,
>>     crashOnError: Bool = false,
>>     errorHandler: CommonErrorHandlerType = {
>>         // Default handler prints context:error and returns nil
>>         fileName, lineNumber, error in
>>         
>>         /// Retrieve last path component because #fileName is
>>         /// not yet a thing in Swift
>>         let trimmedFileName: String = (fileName as NSString).lastPathComponent
>>         
>>         /// Force print and return nil like try?
>>         print("Error \(trimmedFileName):\(lineNumber) \(error)")
>>     },
>>     closure: () throws -> T) -> T? {
>>         
>>         do {
>>             // Return executes only if closure succeeds, returning T
>>             return try closure()
>>             
>>         } catch {
>>             // Emulate try! by crashing
>>             if crashOnError {
>>                 print("Fatal error \(fileName):\(lineNumber): \(error)")
>>                 fatalError()
>>             }
>>             
>>             // Execute error handler and return nil
>>             errorHandler(fileName, lineNumber, error)
>>             return nil
>>         }
>> }
>> 
>> 

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


More information about the swift-evolution mailing list