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

Adrian Zubarev adrian.zubarev at devandartist.com
Fri Feb 5 11:54:47 CST 2016


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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160205/a6eaec2d/attachment.html>


More information about the swift-evolution mailing list