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?