{"id":13428881,"url":"https://github.com/michaelbull/kotlin-result","last_synced_at":"2025-05-14T07:09:44.294Z","repository":{"id":26300118,"uuid":"107745806","full_name":"michaelbull/kotlin-result","owner":"michaelbull","description":"A multiplatform Result monad for modelling success or failure operations.","archived":false,"fork":false,"pushed_at":"2025-05-06T13:06:59.000Z","size":942,"stargazers_count":1136,"open_issues_count":9,"forks_count":71,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-05-06T14:30:07.053Z","etag":null,"topics":["browser","class","either","fp","functional","functional-programming","ios","js","jvm","kotlin","kotlin-multiplatform","monad","result","type"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/michaelbull.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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,"zenodo":null}},"created_at":"2017-10-21T02:42:29.000Z","updated_at":"2025-05-06T13:07:03.000Z","dependencies_parsed_at":"2023-01-14T04:22:08.537Z","dependency_job_id":"7415f9ef-3320-4de6-aeb0-a3c6bb0e7559","html_url":"https://github.com/michaelbull/kotlin-result","commit_stats":{"total_commits":424,"total_committers":30,"mean_commits":"14.133333333333333","dds":"0.11084905660377353","last_synced_commit":"db27396a1f859f91f42f6121eff512e592b7cb4b"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelbull%2Fkotlin-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelbull%2Fkotlin-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelbull%2Fkotlin-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelbull%2Fkotlin-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelbull","download_url":"https://codeload.github.com/michaelbull/kotlin-result/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254092788,"owners_count":22013290,"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":["browser","class","either","fp","functional","functional-programming","ios","js","jvm","kotlin","kotlin-multiplatform","monad","result","type"],"created_at":"2024-07-31T01:01:07.698Z","updated_at":"2025-05-14T07:09:39.283Z","avatar_url":"https://github.com/michaelbull.png","language":"Kotlin","funding_links":[],"categories":["Libraries"],"sub_categories":["Utility"],"readme":"# kotlin-result\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.michael-bull.kotlin-result/kotlin-result.svg)](https://search.maven.org/search?q=g:com.michael-bull.kotlin-result)\n[![CI](https://github.com/michaelbull/kotlin-result/actions/workflows/ci.yaml/badge.svg)](https://github.com/michaelbull/kotlin-result/actions/workflows/ci.yaml)\n[![License](https://img.shields.io/github/license/michaelbull/kotlin-result.svg)](https://github.com/michaelbull/kotlin-result/blob/master/LICENSE)\n\n![badge][badge-android]\n![badge][badge-jvm]\n![badge][badge-js]\n![badge][badge-nodejs]\n![badge][badge-linux]\n![badge][badge-windows]\n![badge][badge-wasm]\n![badge][badge-ios]\n![badge][badge-mac]\n![badge][badge-tvos]\n![badge][badge-watchos]\n![badge][badge-js-ir]\n![badge][badge-android-native]\n![badge][badge-apple-silicon]\n\nA multiplatform Result monad for modelling success or failure operations.\n\n## Installation\n\n```groovy\nrepositories {\n    mavenCentral()\n}\n\ndependencies {\n    implementation(\"com.michael-bull.kotlin-result:kotlin-result:2.0.1\")\n}\n```\n\n## Introduction\n\nIn functional programming, the result [`Result`][result] type is a monadic type\nholding a returned [value][result-value] or an [error][result-error].\n\nTo indicate an operation that succeeded, return an [`Ok(value)`][result-Ok]\nwith the successful `value`. If it failed, return an [`Err(error)`][result-Err]\nwith the `error` that caused the failure.\n\nThis helps to define a clear happy/unhappy path of execution that is commonly\nreferred to as [Railway Oriented Programming][rop], whereby the happy and\nunhappy paths are represented as separate railways.\n\n### Overhead\n\nThe `Result` type is modelled as an\n[inline value class][kotlin-inline-classes]. This achieves zero object\nallocations on the happy path.\n\nA full breakdown, with example output Java code, is available in the\n[Overhead][wiki-Overhead] design doc.\n\n### Multiplatform Support\n\n`kotlin-result` targets all three tiers outlined by the\n[Kotlin/Native target support][kotlin-native-target-support]\n\n### Read More\n\nBelow is a collection of videos \u0026 articles authored on the subject of this\nlibrary. Feel free to open a pull request on [GitHub][github] if you would like\nto include yours.\n\n- [[EN] The Result Monad - Adam Bennett](https://adambennett.dev/2020/05/the-result-monad/)\n- [[EN] A Functional Approach to Exception Handling - Tristan Hamilton](https://youtu.be/bEC_t8dH23c?t=132)\n- [[EN] kotlin: A functional gold mine - Mark Bucciarelli](http://markbucciarelli.com/posts/2020-01-04_kotlin_functional_gold_mine.html)\n- [[EN] Railway Oriented Programming - Scott Wlaschin](https://fsharpforfunandprofit.com/rop/)\n- [[JP] KotlinでResult型使うならkotlin-resultを使おう](https://note.com/telin/n/n6d9e352c344c)\n- [[JP] kotlinのコードにReturn Resultを組み込む](https://nnao45.hatenadiary.com/entry/2019/11/30/224820)\n\nMappings are available on the [wiki][wiki] to assist those with experience\nusing the `Result` type in other languages:\n\n- [Elm](https://github.com/michaelbull/kotlin-result/wiki/Elm)\n- [Haskell](https://github.com/michaelbull/kotlin-result/wiki/Haskell)\n- [Rust](https://github.com/michaelbull/kotlin-result/wiki/Rust)\n- [Scala](https://github.com/michaelbull/kotlin-result/wiki/Scala)\n\n## Getting Started\n\nBelow is a simple example of how you may use the `Result` type to model a\nfunction that may fail.\n\n```kotlin\nfun checkPrivileges(user: User, command: Command): Result\u003cCommand, CommandError\u003e {\n    return if (user.rank \u003e= command.minimumRank) {\n        Ok(command)\n    } else {\n        Err(CommandError.InsufficientRank(command.name))\n    }\n}\n```\n\nWhen interacting with code outside your control that may throw exceptions, wrap\nthe call with [`runCatching`][result-runCatching] to capture its execution as a\n`Result\u003cT, Throwable\u003e`:\n\n```kotlin\nval result: Result\u003cCustomer, Throwable\u003e = runCatching {\n    customerDb.findById(id = 50) // could throw SQLException or similar\n}\n```\n\nNullable types, such as the `find` method in the example below, can be\nconverted to a `Result` using the `toResultOr` extension function.\n\n```kotlin\nval result: Result\u003cCustomer, String\u003e = customers\n    .find { it.id == id } // returns Customer?\n    .toResultOr { \"No customer found\" }\n```\n\n### Transforming Results\n\nBoth success and failure results can be transformed within a stage of the\nrailway track. The example below demonstrates how to transform an internal\nprogram error `UnlockError` into the exposed client error `IncorrectPassword`.\n\n```kotlin\nval result: Result\u003cTreasure, UnlockResponse\u003e =\n    unlockVault(\"my-password\") // returns Result\u003cTreasure, UnlockError\u003e\n    .mapError { IncorrectPassword } // transform UnlockError into IncorrectPassword\n```\n\n### Chaining\n\nResults can be chained to produce a \"happy path\" of execution. For example, the\nhappy path for a user entering commands into an administrative console would\nconsist of: the command being tokenized, the command being registered, the user\nhaving sufficient privileges, and the command executing the associated action.\nThe example below uses the `checkPrivileges` function we defined earlier.\n\n```kotlin\ntokenize(command.toLowerCase())\n    .andThen(::findCommand)\n    .andThen { cmd -\u003e checkPrivileges(loggedInUser, cmd) }\n    .andThen { execute(user = loggedInUser, command = cmd, timestamp = LocalDateTime.now()) }\n    .mapBoth(\n        { output -\u003e printToConsole(\"returned: $output\") },\n        { error  -\u003e printToConsole(\"failed to execute, reason: ${error.reason}\") }\n    )\n```\n\n### Binding (Monad Comprehension)\n\nThe [`binding`][result-binding] function allows multiple calls that each return\na `Result` to be chained imperatively. When inside a `binding` block, the\n`bind()` function is accessible on any `Result`. Each call to `bind` will\nattempt to unwrap the `Result` and store its value, returning early if any\n`Result` is an error.\n\nIn the example below, should `functionX()` return an error, then execution will\nskip both `functionY()` and `functionZ()`, instead storing the error from\n`functionX` in the variable named `sum`.\n\n```kotlin\nfun functionX(): Result\u003cInt, SumError\u003e { ... }\nfun functionY(): Result\u003cInt, SumError\u003e { ... }\nfun functionZ(): Result\u003cInt, SumError\u003e { ... }\n\nval sum: Result\u003cInt, SumError\u003e = binding {\n    val x = functionX().bind()\n    val y = functionY().bind()\n    val z = functionZ().bind()\n    x + y + z\n}\n\nprintln(\"The sum is $sum\") // prints \"The sum is Ok(100)\"\n```\n\nThe `binding` function primarily draws inspiration from\n[Bow's `binding` function][bow-bindings], however below is a list of other\nresources on the topic of monad comprehensions.\n\n- [Monad comprehensions - Arrow (Kotlin)](https://old.arrow-kt.io/docs/patterns/monad_comprehensions/)\n- [Monad comprehensions - Bow (Swift)](https://bow-swift.io/docs/patterns/monad-comprehensions)\n- [For comprehensions - Scala](https://docs.scala-lang.org/tour/for-comprehensions.html)\n\n#### Coroutine Binding Support\n\nUse of suspending functions within a `coroutineBinding` block requires an\nadditional dependency:\n\n```kotlin\ndependencies {\n    implementation(\"com.michael-bull.kotlin-result:kotlin-result:2.0.1\")\n    implementation(\"com.michael-bull.kotlin-result:kotlin-result-coroutines:2.0.1\")\n}\n```\n\nThe [`coroutineBinding`][result-coroutineBinding] function runs inside a\n[`coroutineScope`][kotlin-coroutineScope], facilitating _concurrent\ndecomposition of work_.\n\nWhen any call to `bind()` inside the block fails, the scope fails, cancelling\nall other children.\n\nThe example below demonstrates a computationally expensive function that takes\nfive milliseconds to compute being eagerly cancelled as soon as a smaller\nfunction fails in just one millisecond:\n\n```kotlin\nsuspend fun failsIn5ms(): Result\u003cInt, DomainErrorA\u003e { ... }\nsuspend fun failsIn1ms(): Result\u003cInt, DomainErrorB\u003e { ... }\n\nrunBlocking {\n    val result: Result\u003cInt, BindingError\u003e = coroutineBinding { // this creates a new CoroutineScope\n        val x = async { failsIn5ms().bind() }\n        val y = async { failsIn1ms().bind() }\n        x.await() + y.await()\n    }\n\n    // result will be Err(DomainErrorB)\n}\n```\n\n## Inspiration\n\nInspiration for this library has been drawn from other languages in which the\nResult monad is present, including:\n\n- [Elm](http://package.elm-lang.org/packages/elm-lang/core/latest/Result)\n- [Haskell](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html)\n- [Rust](https://doc.rust-lang.org/std/result/)\n- [Scala](http://www.scala-lang.org/api/2.12.4/scala/util/Either.html)\n\nImprovements on existing solutions such the stdlib include:\n\n- Reduced runtime overhead with zero object allocations on the happy path\n- Feature parity with Result types from other languages including Elm, Haskell,\n     \u0026 Rust\n- Lax constraints on `value`/`error` nullability\n- Lax constraints on the `error` type's inheritance (does not inherit from\n    `Exception`)\n- Top level `Ok` and `Err` functions avoids qualifying usages with\n    `Result.Ok`/`Result.Err` respectively\n- Higher-order functions marked with the `inline` keyword for reduced runtime\n    overhead\n- Extension functions on `Iterable` \u0026 `List` for folding, combining, partitioning\n- Consistent naming with existing Result libraries from other languages (e.g.\n    `map`, `mapError`, `mapBoth`, `mapEither`, `and`, `andThen`, `or`, `orElse`,\n    `unwrap`)\n- Extensive test suite with almost 100 [unit tests][unit-tests] covering every library method\n\n## Example\n\nThe [example][example] module contains an implementation of Scott's\n[example application][swalschin-example] that demonstrates the usage of `Result`\nin a real world scenario.\n\nIt hosts a [ktor][ktor] server on port 9000 with a `/customers` endpoint. The\nendpoint responds to both `GET` and `POST` requests with a provided `id`, e.g.\n`/customers/100`. Upserting a customer id of 42 is hardcoded to throw an\n[`SQLException`][customer-42] to demonstrate how the `Result` type can\n[map internal program errors][update-customer-error] to more appropriate\nuser-facing errors.\n\n## Contributing\n\nBug reports and pull requests are welcome on [GitHub][github].\n\n## License\n\nThis project is available under the terms of the ISC license. See the\n[`LICENSE`](LICENSE) file for the copyright information and licensing terms.\n\n[result]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L10\n[result-value]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L55\n[result-error]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L59\n[result-Ok]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L9\n[result-Err]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L17\n[kotlin-inline-classes]: https://kotlinlang.org/docs/inline-classes.html\n[wiki-Overhead]: https://github.com/michaelbull/kotlin-result/wiki/Overhead\n[rop]: https://fsharpforfunandprofit.com/rop/\n[kotlin-native-target-support]: https://kotlinlang.org/docs/native-target-support.html\n[github]: https://github.com/michaelbull/kotlin-result\n[wiki]: https://github.com/michaelbull/kotlin-result/wiki\n[result-runCatching]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Factory.kt#L11\n[result-binding]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Binding.kt#L28\n[bow-bindings]: https://bow-swift.io/docs/patterns/monad-comprehensions/#bindings\n[result-coroutineBinding]: https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/CoroutineBinding.kt#L42\n[kotlin-coroutineScope]: https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html\n[unit-tests]: https://github.com/michaelbull/kotlin-result/tree/master/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result\n[example]: https://github.com/michaelbull/kotlin-result/tree/master/example/src/main/kotlin/com/github/michaelbull/result/example\n[swalschin-example]: https://github.com/swlaschin/Railway-Oriented-Programming-Example\n[ktor]: http://ktor.io/\n[customer-42]: https://github.com/michaelbull/kotlin-result/blob/master/example/src/main/kotlin/com/github/michaelbull/result/example/repository/InMemoryCustomerRepository.kt#L38\n[update-customer-error]: https://github.com/michaelbull/kotlin-result/blob/master/example/src/main/kotlin/com/github/michaelbull/result/example/service/CustomerService.kt#L50\n\n[badge-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat\n[badge-android-native]: http://img.shields.io/badge/support-[AndroidNative]-6EDB8D.svg?style=flat\n[badge-jvm]: http://img.shields.io/badge/-jvm-DB413D.svg?style=flat\n[badge-js]: http://img.shields.io/badge/-js-F8DB5D.svg?style=flat\n[badge-js-ir]: https://img.shields.io/badge/support-[IR]-AAC4E0.svg?style=flat\n[badge-nodejs]: https://img.shields.io/badge/-nodejs-68a063.svg?style=flat\n[badge-linux]: http://img.shields.io/badge/-linux-2D3F6C.svg?style=flat\n[badge-windows]: http://img.shields.io/badge/-windows-4D76CD.svg?style=flat\n[badge-wasm]: https://img.shields.io/badge/-wasm-624FE8.svg?style=flat\n[badge-apple-silicon]: http://img.shields.io/badge/support-[AppleSilicon]-43BBFF.svg?style=flat\n[badge-ios]: http://img.shields.io/badge/-ios-CDCDCD.svg?style=flat\n[badge-mac]: http://img.shields.io/badge/-macos-111111.svg?style=flat\n[badge-watchos]: http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat\n[badge-tvos]: http://img.shields.io/badge/-tvos-808080.svg?style=flat\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelbull%2Fkotlin-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelbull%2Fkotlin-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelbull%2Fkotlin-result/lists"}