This project adds several new methods to the Kotlin Result
(kotlin.Result<T>
) type to facilitate
more robust, functional-style error handling. The main focus is on chaining Result
operations
together and handling more complex and nested structures.
These extensions unleash all the "monadic power" of kotlin.Result
, making it a
real Monad.
- Add it in your root
build.gradle
at the end of repositories:
allprojects {
repositories {
// ...
maven { url 'https://jitpack.io' }
}
}
- Add the dependency
dependencies {
implementation("com.github.siboxd:kotlin-result-extension:${latestVersion}")
}
These extensions make possible the chaining of operations avoiding Result<Result<T>>
compositions.
Transforms the value of a successful Result
and expects the result of the transformation to return
a Result
. Using just map
, this leads to Result<Result<T>>
, but flatMap
will unwrap the
outer Result
so only the inner one is returned.
val doMoreReturnResult = { value: String ->
Result.success("More: $value")
}
val result = runCatching { "Start Value" }
.flatMap { startValue -> doMoreReturnResult(startValue) }
.getOrThrow()
// `result` is `"More: Start Value"`
Same as flatMap
but catches exceptions that could be thrown by the transforming function.
val result = runCatching { "Start Value" }
.flatMapCatching<String, String> { startValue ->
throw RuntimeException("MyException")
}
// `result` is `Result.failure(RuntimeException("MyException"))`
Similar to the existing recover
function, only it expects a Result
to be returned from the
transformation. Like the others, it will unwrap the Result
to propagate it. Useful for recovering
with operations that return a result.
val result = runCatching { throw RuntimeException("Dying") }
.flatRecover { Result.success("Hello World") }
.getOrThrow()
// `result` is `Hello World`
Same as flatRecover
, but it can handle exceptions being thrown within the function body.
val result = runCatching { throw RuntimeException("Dying") }
.flatRecoverCatching { throw RuntimeException("MyException") }
// `result` is `Result.failure(RuntimeException("MyException"))`
A simple way to unwrap nested Results from chained operations.
val doMoreReturnResult = { value: String ->
Result.success("More: $value")
}
val result = runCatching { "Start Value" }
.map { startValue -> doMoreReturnResult(startValue) }
.flatten()
.getOrThrow()
// "result" is "More: Start Value"
These extensions make possible to conditionally apply a transformation only if a
certain predicate
is satisfied.
If the provided predicate returns true
, transforms the value of a successful Result
into another
value.
val doMoreReturnResult = { value: String -> "More: $value" }
val result = runCatching { "Start Value" }
.mapIf({ it.startsWith("Start") }) { startValue ->
doMoreReturnResult(startValue)
}
.getOrThrow()
// `result` is `"More: Start Value"`
Same as mapIf
but catches exceptions that could be thrown by the predicate
or transform
functions.
val result = runCatching { "Start Value" }
.mapCatchingIf({ it.startsWith("S") }) { startValue ->
throw RuntimeException("MyException")
}
// `result` is `Result.failure(RuntimeException("MyException"))`
If the provided predicate returns true
, recovers the exception of a failed Result
into another
value.
val result = runCatching { throw RuntimeException("Dying") }
.recoverIf({ it is RuntimeException }) { "Hello World" }
.getOrThrow()
// `result` is `Hello World`
Same as recoverIf
, but it can handle exceptions being thrown within the predicate
and transform
functions' body.
val result = runCatching { throw RuntimeException("Dying") }
.recoverCatchingIf({ it is RuntimeException }) {
throw RuntimeException("MyException")
}
// `result` is `Result.failure(RuntimeException("MyException"))`
These extensions combine the utility of flattening extensions with that of conditional ones.
Combines functionalities of mapIf
and flatMap
explained above.
val doMoreReturnResult = { value: String ->
Result.success("More: $value")
}
val result = runCatching { "Start Value" }
.flatMapIf({ it.startsWith("S") }) { startValue ->
doMoreReturnResult(startValue)
}
.getOrThrow()
// `result` is `"More: Start Value"`
Combines functionalities of mapCatchingIf
and flatMapCatching
explained above.
val result = runCatching { "Start Value" }
.flatMapCatchingIf({ it.startsWith("S") }) { startValue ->
throw RuntimeException("MyException")
}
// `result` is `Result.failure(RuntimeException("MyException"))`
Combines functionalities of recoverIf
and flatRecover
explained above.
val result = runCatching { throw RuntimeException("Dying") }
.flatRecoverIf({ it is RuntimeException }) { Result.success("Hello World") }
.getOrThrow()
// `result` is `Hello World`
Combines functionalities of recoverCatchingIf
and flatRecoverCatching
explained above.
val result = runCatching { throw RuntimeException("Dying") }
.flatRecoverCatchingIf({ it is RuntimeException }) { throw RuntimeException("MyException") }
// `result` is `Result.failure(RuntimeException("MyException"))`