[swift-evolution] [Proposal]: Escaping another (unused) scope pyramide with 'guard try catch'

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Feb 5 12:28:58 CST 2016


Right now the error handling mechanism will fall through even if the error was handled inside the catch scope.

enum Error: ErrorType {
	case SomeError
}

func throwingFunc() throws {
	throw Error.SomeError
}

func throwingFuncReturns() throws -> Int {
	return 0
}

do { try throwingFunc() } catch {
	/* do nothing */
}

var num: Int
do { num = try throwingFuncReturns() } catch {
	/* do nothing */
}

print("both fell through")

To guarantee the safety of the execution from my point of view the catch body may not fall through (for single try statements), at least not by default.

I think it is fine to rename the proposed mechanism.

do try throwingFunc() catch { 
	/* handle error */ 
}

do try throwingFunc() catch _ { 
	/* handle error */ 
}

do try throwingFunc() catch pattern { 
	/* handle error */ 
}

do try throwingFunc() catch pattern { 
	/* handle error */ 
}

do let newInstance = try throwingFuncReturns() catch catch pattern where condition { 
	/* handle error */ 
}

do var newMutableInstance = try throwingFuncReturns() catch pattern where condition { 
	/* handle error */ 
}

'Where' clause is significant for catching wider range of errors that might occur.

-- 
Adrian Zubarev
Sent with Airmail

Am 5. Februar 2016 bei 19:05:12, Félix Cloutier (felixcca at yahoo.ca) schrieb:

We could do it without a new guard syntax if `do` didn't need a block statement.

Félix

Le 5 févr. 2016 à 12:54:47, Adrian Zubarev via swift-evolution <swift-evolution at swift.org> a écrit :

Hello dear Swift community,

this proposal might seem like some new syntax sugar, but it also aims to escape the 'do { }' scope from the 'do try catch‘ mechanism while making the existing error handling more powerful.

Lets assume we have some sort of network type, which can throw a ton of different errors:

struct TCPListener {
init(address: String) throws { /* implement */ }

func accept() throws -> TCPConn { /* implement */ }

/* ... */
}

A way of implimentation might look like this:

let listener: TCPListener
do {
listener = try TCPListener("some valid address")

// we could do more work here, but if we need to catch more
// errors we will result in a new PYRAMIDE OF DOOM
} catch {
fatalError()
}

At this point think about the comment inside the 'do { }' scope. Such an application might result in a new pyramide of doom as we know from optional unwrapping before 'guard else' mechanism was introduced.

let clientConn: TCPConn
do {
clientConn = try listener.accept() // save to call accept method
} catch {
fatalError()
} 

As you can see this application might not need the extra 'do' scope at all, and if it does, the 'do try catch' is still there.

I propose a new error handling mechanism that mimics the solution for optional pyramide of doom, which adds a slightly better syntax and removes the unneeded/unused 'do { }' scope (as for the example from above). Not only can this mechanism guarantee the execution of a throwing function without any errors (like a true guard condition) it also can assign returned values to a new constant/variable.

Introducing the 'guard try catch' mechanism:

guard try throwingFunc() catch { 
/* handle error */ 
}

guard try throwingFunc() catch _ { 
/* handle error */ 
}

guard try throwingFunc() catch pattern { 
/* handle error */ 
}

guard try throwingFunc() catch pattern where condition { 
/* handle error */ 
}

guard let newInstance = try throwingFuncReturns() catch ... { 
/* handle error */ 
}

Where '...' represents the different combinations of possible patterns already showed in the first 4 examples.

We also might want the return type to be mutable.

guard var newMutableInstance = try throwingFuncReturns() catch ... { 
/* handle error */ 
}

This mechanism also makes the error handling more powerful, since it can catch more specific errors defined with 'where condition'.

Lets rebuild the example from above with the new mechanism:

guard let listener = try TCPListener("some valid address") catch {
fatalError()
}

guard let clientConn = try listener.accept() catch {
fatalError()
} 

One think that should be mentioned here is that the method call from the second 'guard' is safe, because a 'guard' body may not fall through.

Impact on existing codebase: None, because the mechanism is new and does not break any existing code.

I'm really curious about your opinions.
_______________________________________________
swift-evolution mailing list
swift-evolution at swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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


More information about the swift-evolution mailing list