在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):kittinunf/Result开源软件地址(OpenSource Url):https://github.com/kittinunf/Result开源编程语言(OpenSource Language):Kotlin 100.0%开源软件介绍(OpenSource Introduction):ResultThis is a tiny framework for modelling success/failure of operations
in Kotlin. In short, it is a model in type
of Ideology
InstallationGradlerepositories {
mavenCentral()
}
dependencies {
// if you are working on JVM or Android only project
implementation("com.github.kittinunf.result:result-jvm:«version»") //for JVM support
// if you are working in KMM project
implementation("com.github.kittinunf.result:result:«version»") //for Kotlin Multiplatform support
}
TL;DRThis model is highly inspired by "Railway Oriented Programming" concept.
Result.of<T, Throwable>(doOperation())
.flatMap { normalizedData(it) }
.map { createRequestFromData(it) }
.flatMap { database.updateFromRequest(it) } Work with //multi-declaration
val (value, error) = result
//get
val value: Int = result.get() // throw exception if error
//terminal operator
//success
result.success {
}
//failure
result.failure {
}
//fold is there, if you want to handle both success and failure
result.fold({ value ->
//do something with value
}, { err ->
//do something with err
}) Why
Let's consider a need to read data from fun process(): String {
try {
val foo = File("/path/to/file/foo.txt").readText()
val isSuccessful = processData(foo)
if (!isSuccessful) {
return "Data is corrupted and cannot be processed"
}
} catch (e: Throwable) {
//do something if error
Logger.log(ERROR, e.message())
}
} However, things start getting ugly when we have chain of operations being run sequentially, such as fun process(): String {
try {
val foo = File("/path/to/file/foo.txt").readText()
val isSuccessful = normalizedData(foo)
if (!isSuccessful) {
return "Data cannot be processable"
}
val request = createRequestFromData(foo)
try {
val result = database.updateFromRequest(request)
if (!result) {
return "Record in DB is not found"
}
} catch (dbEx: DBThrowable) {
return "DB error, cannot update"
}
} catch (e: Throwable) {
//do something if error
Logger.log(ERROR, e.message())
}
} Ouch, it looks pretty bleak. Let's see how First, we break things down into a small set of model in
val operation = { File("/path/to/file/foo.txt").readText() }
Result.of { operation() } // Result<String, FileThrowable>
fun normalizedData(foo): Result<Boolean, NormalizedThrowable> {
Result.of { foo.normalize() }
}
fun createRequestFromData(foo): Request {
return createRequest(foo)
}
fun database.updateFromRequest(request): Result<Boolean, DBThrowable> {
val transaction = request.transaction
return Result.of {
db.openTransaction {
val success = db.execute(transaction)
if (!success) {
throw DBThrowable("Error")
}
return success
}
}
} The whole operation can be chained by the following; Result.of { doOperation() }
.flatMap { normalizedData(it) }
.map { createRequestFromData(it) }
.flatMap { database.updateFromRequest(it) } The creates a nice "happy path" of the whole chain, also handle error as appropriate. It looks better and cleaner, right?. Never Fail OperationIn some case, one wants to model an always successful operation. // Add operation can never be failure
fun add(i: Int, j: Int) : Result<Int, NoException> Nice thing about modelling in this way is to be able to compose it with others "fail-able"
operations in High Order functionsSuccess
Failure
More featuresPlease check out more features in the ResultTest Railway Oriented ProgrammingIf interested, here are more articles that one might enjoy.
Credit to Scott Wlaschin CreditsResult is brought to you by contributors. LicenseResult is released under the MIT license. |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论