{"id":21963628,"url":"https://github.com/kittinunf/result","last_synced_at":"2025-05-15T17:07:43.304Z","repository":{"id":1991451,"uuid":"45111154","full_name":"kittinunf/Result","owner":"kittinunf","description":"The modelling for success/failure of operations in Kotlin and KMM (Kotlin Multiplatform Mobile)","archived":false,"fork":false,"pushed_at":"2024-07-19T09:08:27.000Z","size":620,"stargazers_count":935,"open_issues_count":1,"forks_count":55,"subscribers_count":15,"default_branch":"master","last_synced_at":"2025-05-15T17:07:35.659Z","etag":null,"topics":["functional","kotlin","kotlin-multiplatform","kotlin-multiplatform-library","optional","result"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kittinunf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["kittinunf"]}},"created_at":"2015-10-28T12:34:53.000Z","updated_at":"2025-04-05T09:47:12.000Z","dependencies_parsed_at":"2023-07-05T16:00:21.750Z","dependency_job_id":"53670ce2-65c6-4ba7-96a3-7e30bd8d7636","html_url":"https://github.com/kittinunf/Result","commit_stats":{"total_commits":153,"total_committers":17,"mean_commits":9.0,"dds":0.3594771241830066,"last_synced_commit":"4a5867b5ecff3e83e61c0d250a32e7d42f06be0f"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kittinunf%2FResult","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kittinunf%2FResult/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kittinunf%2FResult/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kittinunf%2FResult/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kittinunf","download_url":"https://codeload.github.com/kittinunf/Result/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254384988,"owners_count":22062422,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional","kotlin","kotlin-multiplatform","kotlin-multiplatform-library","optional","result"],"created_at":"2024-11-29T11:05:56.850Z","updated_at":"2025-05-15T17:07:38.297Z","avatar_url":"https://github.com/kittinunf.png","language":"Kotlin","readme":"# Result\n\n[![Kotlin](https://img.shields.io/badge/kotlin-1.9.20-blue.svg)](http://kotlinlang.org)\n[![MavenCentral](https://maven-badges.herokuapp.com/maven-central/com.github.kittinunf.result/result-jvm/badge.svg)](https://search.maven.org/search?q=g:com.github.kittinunf.result)\n![Run Gradle](https://github.com/kittinunf/Result/workflows/Run%20Gradle%20on%20Main/badge.svg?branch=master)\n[![Codecov](https://codecov.io/github/kittinunf/Result/coverage.svg?branch=master)](https://codecov.io/gh/kittinunf/Result)\n\nThis is a tiny framework for modelling success/failure of operations\nin [Kotlin](http://kotlinlang.org). In short, it is a model in type\nof `Result\u003cV: Any?, E : Throwable\u003e`.\n\n## Ideology\n\n`Result\u003cV: Any?, E: Throwable\u003e` is to provide higher abstraction of operation that can be ended with\nresult either success or failure. `Result.Success` represents `value` in case of success,\nand `Result.Failure` represents `error` in case of failure which is upper bounded with `Throwable`\ntype.\n\n## Installation\n\n### Gradle\n\n``` Groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    // if you are working on JVM or Android only project\n    implementation(\"com.github.kittinunf.result:result-jvm:«version»\") //for JVM support\n    \n    // if you are working in KMM project\n    implementation(\"com.github.kittinunf.result:result:«version»\") //for Kotlin Multiplatform support\n}\n\n```\n\n## TL;DR\n\nThis model is highly inspired\nby \"[Railway Oriented Programming](http://fsharpforfunandprofit.com/rop/#monads)\" concept.\n\n`Result` allows one to express series of success/failure operations in Kotlin as;\n\n``` Kotlin\nResult.of\u003cT, Throwable\u003e(doOperation())\n      .flatMap { normalizedData(it) }\n      .map { createRequestFromData(it) }\n      .flatMap { database.updateFromRequest(it) }\n```\n\nWork with `Result` is easy\n\n``` Kotlin\n//multi-declaration\nval (value, error) = result\n\n//get\nval value: Int = result.get() // throw exception if error\n\n//terminal operator\n//success\nresult.success {\n}\n\n//failure\nresult.failure {\n}\n\n//fold is there, if you want to handle both success and failure\nresult.fold({ value -\u003e\n    //do something with value\n}, { err -\u003e\n    //do something with err\n})\n```\n\n## Why\n\n`Result` is suitable whenever there is a need to represent an operation that has the possibility of\nfailure. Error handling can be cumbersome to work with.\n`Result` helps process the operations in a nice, functional way, while maintaining readability to\nyour code.\n\nLet's consider a need to read data from `foo`, and to perform some further validation\n\n``` Kotlin\nfun process(): String {\n    try {\n        val foo = File(\"/path/to/file/foo.txt\").readText()\n        val isSuccessful = processData(foo)\n        if (!isSuccessful) {\n            return \"Data is corrupted and cannot be processed\"\n        }\n    } catch (e: Throwable) {\n        //do something if error\n        Logger.log(ERROR, e.message())\n    }\n}\n```\n\nHowever, things start getting ugly when we have chain of operations being run sequentially, such as\n\n``` Kotlin\nfun process(): String {\n    try {\n        val foo = File(\"/path/to/file/foo.txt\").readText()\n        val isSuccessful = normalizedData(foo)\n        if (!isSuccessful) {\n            return \"Data cannot be processable\"\n        }\n        val request = createRequestFromData(foo)\n        try {\n            val result = database.updateFromRequest(request)\n            if (!result) {\n                return \"Record in DB is not found\"\n            }\n        } catch (dbEx: DBThrowable) {\n            return \"DB error, cannot update\"\n        }\n    } catch (e: Throwable) {\n        //do something if error\n        Logger.log(ERROR, e.message())\n    }\n}\n```\n\nOuch, it looks pretty bleak.\n\nLet's see how `Result` can help us.\n\nFirst, we break things down into a small set of model in `Result`.\n\n* Read a file\n\n``` Kotlin\nval operation = { File(\"/path/to/file/foo.txt\").readText() }\nResult.of { operation() }  // Result\u003cString, FileThrowable\u003e\n```\n\n* Normalize a data\n\n``` Kotlin\nfun normalizedData(foo): Result\u003cBoolean, NormalizedThrowable\u003e {\n    Result.of { foo.normalize() }\n}\n```\n\n* Create a request from data\n\n``` Kotlin\nfun createRequestFromData(foo): Request {\n    return createRequest(foo)\n}\n```\n\n* Update DB with Request\n\n``` Kotlin\nfun database.updateFromRequest(request): Result\u003cBoolean, DBThrowable\u003e {\n    val transaction = request.transaction\n    return Result.of { \n        db.openTransaction {\n            val success = db.execute(transaction)\n            if (!success) {\n                throw DBThrowable(\"Error\")\n            }\n            return success\n        }\n    }\n}\n```\n\nThe whole operation can be chained by the following;\n\n``` Kotlin\nResult.of { doOperation() }\n      .flatMap { normalizedData(it) }\n      .map { createRequestFromData(it) }\n      .flatMap { database.updateFromRequest(it) }\n```\n\nThe creates a nice \"happy path\" of the whole chain, also handle error as appropriate. It looks\nbetter and cleaner, right?.\n\n## Never Fail Operation\n\nIn some case, one wants to model an always successful operation. `Result\u003cV: Any?, NoException\u003e` is a\ngood idea for that.\n`NoException` is to indicate that there is no exception to throw. E.g.\n\n``` Kotlin\n// Add operation can never be failure\nfun add(i: Int, j: Int) : Result\u003cInt, NoException\u003e\n```\n\nNice thing about modelling in this way is to be able to compose it with others \"fail-able\"\noperations in `Result`.\n\n## High Order functions\n\n### Success\n\n`map` and `flatMap`\n\n`map` transforms `Result` with given transformation `(V) -\u003e U`. As a result, we are able to\ntransform `V` into a new `V` in the case where `Result` is `Result.Success`. When `Result`\nis `Result.Failure`, `error` is re-wrapped into a new `Result`.\n\n`flatMap` is similar to `map`, however it requires transformation in type of `(V) -\u003e Result\u003cU, ...\u003e`\n.\n\n### Failure\n\n`mapError` and `flatMapError`\n\n`mapError` (`(E) -\u003e E2`) and `flatMapError` (`(E) -\u003e Result\u003cE2, ...\u003e`) are counterpart of `map`\nand `flatMap`. However, they are operate on `Result.Failure`. It is quite handy when one needs to do\nsome transformation on given `Throwable` into a custom type of `Throwable` that suits ones' need.\n\n## More features\n\nPlease check out more features in\nthe [ResultTest](./result/src/commonTest/kotlin/com/github/kittinunf/result/ResultTest.kt)\n\n## Railway Oriented Programming\n\nIf interested, here are more articles that one might enjoy.\n\n* [Recipe Part 2](http://fsharpforfunandprofit.com/posts/recipe-part2/)\n* [Approach Error Handing](https://speakerdeck.com/swlaschin/railway-oriented-programming-a-functional-approach-to-error-handling)\n* [Example](https://github.com/swlaschin/Railway-Oriented-Programming-Example)\n\nCredit to Scott Wlaschin\n\n## Credits\n\nResult is brought to you by [contributors](https://github.com/kittinunf/Result/graphs/contributors).\n\n## License\n\nResult is released under the [MIT](http://opensource.org/licenses/MIT) license.\n","funding_links":["https://github.com/sponsors/kittinunf"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkittinunf%2Fresult","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkittinunf%2Fresult","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkittinunf%2Fresult/lists"}