I'm curious what others think about being able to "retarget" throws. Here's the problem...
There are use-cases where you have a completion handler that takes a Result argument, something like this:
func doSomeAsyncThing(completion: (Result<SomeSuccessObject,Error>)->Void) -> Void {
...
}
The pattern inside this function then can have a lot of error checking and staggered completion, something like this:
func doSomeAsyncThing(completion: (Result<SomeSuccessObject,Error>)->Void) -> Void {
if let error = someErrorCondition {
completion(.failure(error))
return
}
if ...someOtherErrorCheck... {
completion(.failure(someOtherError))
} else if ...someOtherCondition... {
if ...maybeMoreConditions... {
completion(.success(successObject))
} else {
completion(.failure(someOtherError2))
}
}
}
This ends up being a little bit tedious... I've found when it gets large enough, it's sometimes more readable to wrap the whole thing in a do..catch block like so:
func doSomeAsyncThing(completion: (Result<SomeSuccessObject,Error>)->Void) -> Void {
do {
if let error = someErrorCondition {
throw error
}
if ...someOtherErrorCheck... {
throw someOtherError
} else if ...someOtherCondition... {
if ...maybeMoreConditions... {
completion(.success(successObject))
} else {
throw someOtherError2
}
}
} catch {
completion(.failure(error))
}
completion(.success(AnotherSuccessObject))
}
What I'm wondering is whether this case warrants some additional syntactical help... Perhaps the introduction of a throwinto
keyword (or something like it), to stand in for the do/catch lines. Something like:
func doSomeAsyncThing(completion: throwinto (Result<SomeSuccessObject,Error>)->Void) -> Void {
if let error = someErrorCondition {
throw error // implies: completion(.failure(error)); return
}
if ...someOtherErrorCheck... {
throw someOtherError
} else if ...someOtherCondition... {
if ...maybeMoreConditions... {
completion(.success(successObject))
} else {
throw someOtherError2
}
}
}
This could do the same thing for functions that return a Result type:
func doSomething() -> throwinto Result<SomeSuccessObject,Error>
Of course throw can only have one target, so any given function can specify only one of either throwinto
or throws
, but not both and not more than one of each.
It doesn't seem like much but I think it helps to clarify intent and reduce some clutter in the code.
Thoughts?