Add a function combining andThen with runCatching
oddsund opened this issue Β· comments
Hello,
First of all, sorry for not fixing that typo in the PR i sent you π
A common pattern I see when wrapping library code is the following;
.andThen {
runCatching {
// lib code
}
}
The following creates an annoying typewarning, and looks wonky;
.map {
// code
}.runCatching {
if(this is Err) {
throw this.error
} else {
// lib code
}
}
Would you be willing to accept a PR for something like this? Name TBA, fire if you have any suggestions.
Also, would you place such a function in And.kt or Factory.kt?
.andThenCatching {
// lib code
}
.runCatching {
if(it is Err) {
throw it
} else {
// lib code
}
}
This part of your example doesn't make sense to me. What is it
in this context?
The current implementation of runCatching
has no argument, therefore has no it
:
public inline fun <V> runCatching(block: () -> V): Result<V, Throwable>
Generally to solve your problem I would make my own function that calls runCatching
on the library code, rather than nesting it as per your example.
sealed class StepError {
object One : StepError()
object Two : StepError()
data class Three(val message: String?) : StepError()
}
fun runAllSteps(input: Int): Result<Int, StepError> {
return stepOne(input)
.andThen(::stepTwo)
.andThen(::stepThree)
}
fun stepOne(input: Int): Result<Int, StepError.One> {
TODO()
}
fun stepTwo(input: Int): Result<Int, StepError.Two> {
TODO()
}
fun stepThree(input: Int): Result<Int, StepError.Three> {
return callVendor(input).mapError { StepError.Three(it.message) }
}
fun callVendor(input: Int): Result<Int, Throwable> = runCatching {
TODO()
}
In the above example, the runAllSteps
function is transparent about the steps in the process, but stepThree
hides the implementation detail of calling a third party library in its body. If you decide at a later point to change which library step three is calling behind-the-scenes, you only need to edit stepThree
and your runAllSteps
function remains as it was.
Sorry, updated the example. The example used the follwing call;
public inline infix fun <T, V> T.runCatching(block: T.() -> V): Result<V, Throwable> {
So it
was supposed to be this
, as one would have if the call was chained.
I've already solved it (kind of) like that a few times, so I guess I'll keep going that way. Still learning, so thanks for the tip!
No worries. Generally I'm apprehensive to changes like this because we would effectively end up going through the whole library adding xCatching
variants for every function, in order for the library to comprehensively handle the cases where you're working with code that throws instead of returning a Result
. I'd rather keep the library more focused and encourage people to be writing their code first-and-foremost with Result
in mind, and only using things like runCatching
when interacting with code they can't control.
Feel free to reach out with more cool ideas though, I'm open to changing my mind!