1919![ badge] [ badge-android-native ]
2020![ badge] [ badge-apple-silicon ]
2121
22- A multiplatform Result monad for modelling success or failure operations.
22+ A multiplatform Result monad for modelling success or failure operations, providing all three tiers
23+ of [ Kotlin/Native target support] [ kotlin-native-target-support ] .
2324
2425## Installation
2526
@@ -44,38 +45,6 @@ return an [`Err(error)`][result-Err] with the `error` that caused the failure.
4445This helps to define a clear happy/unhappy path of execution that is commonly referred to
4546as [ Railway Oriented Programming] [ rop ] , whereby the happy and unhappy paths are represented as separate railways.
4647
47- ### Overhead
48-
49- The ` Result ` type is modelled as an [ inline value class] [ kotlin-inline-classes ] . This achieves zero object allocations
50- on the happy path.
51-
52- A full breakdown, with example output Java code, is available in the [ Overhead] [ wiki-Overhead ] design doc.
53-
54- ### Multiplatform Support
55-
56- ` kotlin-result ` targets all three tiers outlined by the [ Kotlin/Native target support] [ kotlin-native-target-support ]
57-
58- ### Read More
59-
60- Below is a collection of videos & articles authored on the subject of this library. Feel free to open a pull request
61- on [ GitHub] [ github ] if you would like to include yours.
62-
63- - [[ EN] The Result Monad - Adam Bennett] ( https://adambennett.dev/2020/05/the-result-monad/ )
64- - [[ EN] A Functional Approach to Exception Handling - Tristan Hamilton] ( https://youtu.be/bEC_t8dH23c?t=132 )
65- - [[ EN] kotlin: A functional gold mine - Mark Bucciarelli] ( http://markbucciarelli.com/posts/2020-01-04_kotlin_functional_gold_mine.html )
66- - [[ EN] Railway Oriented Programming - Scott Wlaschin] ( https://fsharpforfunandprofit.com/rop/ )
67- - [[ JP] KotlinでResult型使うならkotlin-resultを使おう] ( https://note.com/telin/n/n6d9e352c344c )
68- - [[ JP] kotlinのコードにReturn Resultを組み込む] ( https://nnao45.hatenadiary.com/entry/2019/11/30/224820 )
69- - [[ JP] kotlin-resultを半年使ってみて] ( https://zenn.dev/loglass/articles/try-using-kotlin-result )
70- - [[ JP] kotlin-result入門] ( https://blog.nnn.dev/entry/2023/06/22/110000 )
71-
72- Mappings are available on the [ wiki] [ wiki ] to assist those with experience using the ` Result ` type in other languages:
73-
74- - [ Elm] ( https://github.com/michaelbull/kotlin-result/wiki/Elm )
75- - [ Haskell] ( https://github.com/michaelbull/kotlin-result/wiki/Haskell )
76- - [ Rust] ( https://github.com/michaelbull/kotlin-result/wiki/Rust )
77- - [ Scala] ( https://github.com/michaelbull/kotlin-result/wiki/Scala )
78-
7948## Getting Started
8049
8150Below is a simple example of how you may use the ` Result ` type to model a function that may fail.
@@ -90,7 +59,7 @@ fun checkPrivileges(user: User, command: Command): Result<Command, CommandError>
9059}
9160```
9261
93- When interacting with code outside your control that may throw exceptions, wrap the call
62+ When interacting with code outside of your control that may throw exceptions, wrap the call
9463with [ ` runCatching ` ] [ result-runCatching ] to capture its execution as a ` Result<T, Throwable> ` :
9564
9665``` kotlin
@@ -137,25 +106,20 @@ tokenize(command.toLowerCase())
137106 )
138107```
139108
140- ### Binding (Monad Comprehension)
109+ ## Advanced Usage - Binding (Monad Comprehension)
141110
142111The [ ` binding ` ] [ result-binding ] function allows multiple calls that each return a ` Result ` to be chained imperatively.
112+
143113When inside a ` binding ` block, the ` bind() ` function is accessible on any ` Result ` . Each call to ` bind ` will attempt to
144114unwrap the ` Result ` and store its value, returning early if any ` Result ` is an error.
145115
146116In the example below, should ` functionX() ` return an error, then execution will skip both ` functionY() ` and
147117` functionZ() ` , instead storing the error from ` functionX ` in the variable named ` sum ` .
148118
149119``` kotlin
150- fun functionX (): Result <Int , SumError > {
151- .. .
152- }
153- fun functionY (): Result <Int , SumError > {
154- .. .
155- }
156- fun functionZ (): Result <Int , SumError > {
157- .. .
158- }
120+ fun functionX (): Result <Int , SumError > = TODO ()
121+ fun functionY (): Result <Int , SumError > = TODO ()
122+ fun functionZ (): Result <Int , SumError > = TODO ()
159123
160124val sum: Result <Int , SumError > = binding {
161125 val x = functionX().bind()
@@ -194,12 +158,8 @@ The example below demonstrates a computationally expensive function that takes f
194158eagerly cancelled as soon as a smaller function fails in just one millisecond:
195159
196160``` kotlin
197- suspend fun failsIn5ms (): Result <Int , DomainErrorA > {
198- .. .
199- }
200- suspend fun failsIn1ms (): Result <Int , DomainErrorB > {
201- .. .
202- }
161+ suspend fun failsIn5ms (): Result <Int , DomainErrorA > = TODO ()
162+ suspend fun failsIn1ms (): Result <Int , DomainErrorB > = TODO ()
203163
204164runBlocking {
205165 val result: Result <Int , BindingError > = coroutineBinding { // this creates a new CoroutineScope
@@ -212,7 +172,122 @@ runBlocking {
212172}
213173```
214174
215- ## Inspiration
175+ ## FAQs
176+
177+ ### 1. What is the performance cost?
178+
179+ The ` Result ` type is modelled as an [ inline value class] [ kotlin-inline-classes ] . This achieves zero object allocations
180+ on the happy path. A full breakdown, with example output Java code, is available in the [ Overhead] [ wiki-Overhead ] design
181+ doc.
182+
183+ ### 2. Why not use ` kotlin.Result ` from the standard library?
184+
185+ > "` kotlin.Result ` is half-baked"
186+ >
187+ > — [ Ilmir Usmanov, JetBrains] [ stdlib-result-half-baked ]
188+
189+ This library was created in Oct 2017. The JetBrains team introduced ` kotlin.Result ` to the standard library in version
190+ 1.3 of the language in Oct 2018 as an experimental feature. Initially, it was limited to internal use only as it was
191+ "intended to be used by compiler generated code only - namely coroutines".
192+
193+ Less than one week after stating that "we do not encourage use of kotlin.Result", the JetBrains team announced that they
194+ [ "will allow returning kotlin.Result from functions"] [ stdlib-result-return-type-lifted ] . This came at the time when they
195+ were considering guiding users towards contextual receivers to replace the Result paradigm. In later years, the context
196+ receivers experiment was superseded by the more recent context parameters, which are still in an experimental state.
197+
198+ Michail Zarečenskij, the Lead Language Designer for Kotlin, announced at KotlinConf 2025 the development of
199+ [ "Rich Errors in Kotlin"] ( https://2025.kotlinconf.com/talks/762779/ ) , providing yet another potential solution for error
200+ handling.
201+
202+ As of the time of writing, the KEEP for ` kotlin.Result ` states that it is [ "not designed to represent domain-specific
203+ error conditions"] [ stdlib-result-keep ] . This statement should help to inform most users with their decision of adopting
204+ it as a return type for generic business logic.
205+
206+ > "The Result class is not designed to represent domain-specific error conditions."
207+ >
208+ > — [ Kotlin Evolution and Enhancement Process #127 ] [ stdlib-result-keep ]
209+
210+ #### Reasons against ` kotlin.Result ` :
211+
212+ - The functionality it provides does not match that of a first class citizen Result type found in other languages, nor
213+ the functionality offered by this library.
214+ - The Kotlin team admits its "half-baked" and discourages use for "domain-specific error conditions".
215+ - The Kotlin team do not use it, and are sending increasingly mixed messages on how users should be dealing with
216+ domain-specific errors.
217+ - The Kotlin team keep inventing their own domain-specific versions, e.g. [ ` ChannelResult ` ] [ ChannelResult ] ) in the
218+ coroutines library, thus proving the need for such a type but lacking commitment to a standardised solution.
219+ - It was initially unusable as a return type and usage was discouraged. This restriction was then lifted and users
220+ guided towards context receivers. Context receivers were abandoned in favour of the (still experimental) context
221+ parameters. Rich errors have been proposed to supersede context parameters by providing a language-level solution.
222+ - The [ ` runCatching ` ] [ stdlib-result-runCatching ] implementation is ** incompatible** with cooperatively cancelled
223+ coroutines. It catches all child types of ` Throwable ` , therefore catching a ` CancellationException ` . This is a special
224+ type of exception that [ "indicates normal cancellation of a coroutine"] [ CancellationException ] . Catching and not
225+ rethrowing it ** will break** this behaviour. This library provides [ ` runSuspendCatching ` ] [ result-runSuspendCatching ]
226+ to address this.
227+ - Error types are constrained to being subclasses of ` Throwable ` . This means you must inherit from ` Throwable ` in all of
228+ your domain-specific errors. This comes with the trappings of stacktraces being computed per-instantiation, and errors
229+ now being throwable generally across your codebase regardless of whether you intend for consumers to throw them.
230+ - Instantiation is verbose with factory functions being under the ` Result ` companion object: ` Result.success ` ,
231+ ` Result.failure `
232+
233+ #### Reasons for ` kotlin-result ` over ` kotlin.Result ` :
234+
235+ - Consistent naming with existing Result libraries from other languages (e.g. ` map ` , ` mapError ` , ` mapBoth ` , ` mapEither ` ,
236+ ` and ` , ` andThen ` , ` or ` , ` orElse ` , ` unwrap ` )
237+ - Feature parity with Result types from other languages including Elm, Haskell, & Rust
238+ - Extension functions on ` Iterable ` & ` List ` for folding, combining, partitioning
239+ - Monadic comprehension support via the ` binding ` and ` coroutineBinding ` functions for imperative use
240+ - Coroutine-aware primitives e.g. ` coroutineBinding ` and ` runSuspendCatching `
241+ - Lax constraints on the ` error ` type's inheritance (does not inherit from ` Throwable ` )
242+ - Top-level ` Ok ` and ` Err ` functions for instantiation brevity
243+
244+ ### 3. Why not call it ` Either ` ?
245+
246+ > "` Either ` in particular, wow it is just not a beautiful thing. It does not mean OR. It's got a left and a right, it
247+ > should have been called 'left right thingy'. Then you'd have a better sense of the true semantics; there are no
248+ > semantics except what you superimpose on top of it."
249+ >
250+ > — [ Rich Hickey, author of Closure] [ rich-hickey-maybe-not ]
251+
252+ ` Result ` is opinionated in name and nature with a strict definition. It models its success as the left generic
253+ parameter and failure on the right. This decision removes the need for users to choose a "biased" side which is a
254+ repeated point of contention for anyone using the more broadly named ` Either ` type. As such there is no risk of
255+ different libraries/teams/projects using different sides for bias.
256+
257+ ` Either ` itself is misleading and harmful. It is a naive attempt to add a true ` OR ` type to the type system. It has no
258+ pre-defined semantics, and is missing the properties of a truly mathematical ` OR ` :
259+
260+ - ** Not Commutative** : ` Either<String, Int> ` is not the same as the type ` Either<Int, String> ` . The order of the types
261+ is fixed, as the positions themselves have different conventional meanings.
262+ - ** Not Symmetric** : ` Either<String, Int> ` has left and right components are not treated as equals. They are designed
263+ for different roles: ` String ` for the success value and ` Int ` for the error value. They are not interchangeable.
264+
265+ ### 4. Why does [ ` runCatching ` ] [ result-runCatching ] catch ` Throwable ` ?
266+
267+ For consistency with the standard libraries own [ ` runCatching ` ] [ stdlib-result-runCatching ] .
268+
269+ To address the issue of breaking coroutine cancellation behaviour, we introduced the
270+ [ ` runSuspendCatching ` ] [ result-runSuspendCatching ] variant which explicitly rethrows any
271+ [ ` CancellationException ` ] [ CancellationException ] .
272+
273+ Should you need to rethrow a specific type of throwable, use [ ` throwIf ` ] [ result-throwIf ] :
274+
275+ ``` kotlin
276+ runCatching(block).throwIf { error ->
277+ error is IOException
278+ }
279+ ```
280+
281+ ### 5. I've used ` Result ` in another language, how does it translate?
282+
283+ Mappings are available on the [ wiki] [ wiki ] to assist those with experience using the ` Result ` type in other languages:
284+
285+ - [ Elm] ( https://github.com/michaelbull/kotlin-result/wiki/Elm )
286+ - [ Haskell] ( https://github.com/michaelbull/kotlin-result/wiki/Haskell )
287+ - [ Rust] ( https://github.com/michaelbull/kotlin-result/wiki/Rust )
288+ - [ Scala] ( https://github.com/michaelbull/kotlin-result/wiki/Scala )
289+
290+ ### 6. What other languages & libraries inspired this one?
216291
217292Inspiration for this library has been drawn from other languages in which the Result monad is present, including:
218293
@@ -227,13 +302,27 @@ Improvements on existing solutions such the stdlib include:
227302- Feature parity with Result types from other languages including Elm, Haskell, & Rust
228303- Lax constraints on ` value ` /` error ` nullability
229304- Lax constraints on the ` error ` type's inheritance (does not inherit from ` Exception ` )
230- - Top level ` Ok ` and ` Err ` functions avoids qualifying usages with ` Result.Ok ` /` Result.Err ` respectively
305+ - Top- level ` Ok ` and ` Err ` functions avoids qualifying usages with ` Result.Ok ` /` Result.Err ` respectively
231306- Higher-order functions marked with the ` inline ` keyword for reduced runtime overhead
232307- Extension functions on ` Iterable ` & ` List ` for folding, combining, partitioning
233308- Consistent naming with existing Result libraries from other languages (e.g. ` map ` , ` mapError ` , ` mapBoth ` , ` mapEither ` ,
234309 ` and ` , ` andThen ` , ` or ` , ` orElse ` , ` unwrap ` )
235310- Extensive test suite with almost 100 [ unit tests] [ unit-tests ] covering every library method
236311
312+ ### 7. Where can I learn more?
313+
314+ Below is a collection of videos & articles authored on the subject of this library. Feel free to open a pull request
315+ on [ GitHub] [ github ] if you would like to include yours.
316+
317+ - [[ EN] The Result Monad - Adam Bennett] ( https://adambennett.dev/2020/05/the-result-monad/ )
318+ - [[ EN] A Functional Approach to Exception Handling - Tristan Hamilton] ( https://youtu.be/bEC_t8dH23c?t=132 )
319+ - [[ EN] kotlin: A functional gold mine - Mark Bucciarelli] ( http://markbucciarelli.com/posts/2020-01-04_kotlin_functional_gold_mine.html )
320+ - [[ EN] Railway Oriented Programming - Scott Wlaschin] ( https://fsharpforfunandprofit.com/rop/ )
321+ - [[ JP] KotlinでResult型使うならkotlin-resultを使おう] ( https://note.com/telin/n/n6d9e352c344c )
322+ - [[ JP] kotlinのコードにReturn Resultを組み込む] ( https://nnao45.hatenadiary.com/entry/2019/11/30/224820 )
323+ - [[ JP] kotlin-resultを半年使ってみて] ( https://zenn.dev/loglass/articles/try-using-kotlin-result )
324+ - [[ JP] kotlin-result入門] ( https://blog.nnn.dev/entry/2023/06/22/110000 )
325+
237326## Contributing
238327
239328Bug reports and pull requests are welcome on [ GitHub] [ github ] .
@@ -243,6 +332,7 @@ Bug reports and pull requests are welcome on [GitHub][github].
243332This project is available under the terms of the ISC license. See the [ ` LICENSE ` ] ( LICENSE ) file for the copyright
244333information and licensing terms.
245334
335+ [ // ] : # ( @formatter:off )
246336[ result ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L10
247337[ result-value ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L55
248338[ result-error ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Result.kt#L59
@@ -255,11 +345,20 @@ information and licensing terms.
255345[ github ] : https://github.com/michaelbull/kotlin-result
256346[ wiki ] : https://github.com/michaelbull/kotlin-result/wiki
257347[ result-runCatching ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Factory.kt#L11
348+ [ result-runSuspendCatching ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/RunSuspendCatching.kt#L17
349+ [ result-throwIf ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Or.kt#L52
258350[ result-binding ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result/src/commonMain/kotlin/com/github/michaelbull/result/Binding.kt#L28
259351[ bow-bindings ] : https://bow-swift.io/docs/patterns/monad-comprehensions/#bindings
260352[ result-coroutineBinding ] : https://github.com/michaelbull/kotlin-result/blob/master/kotlin-result-coroutines/src/commonMain/kotlin/com/github/michaelbull/result/coroutines/CoroutineBinding.kt#L42
261353[ kotlin-coroutineScope ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html
262354[ unit-tests ] : https://github.com/michaelbull/kotlin-result/tree/master/kotlin-result/src/commonTest/kotlin/com/github/michaelbull/result
355+ [ stdlib-result-half-baked ] : https://discuss.kotlinlang.org/t/state-of-kotlin-result-vs-kotlin-result/21103/4
356+ [ stdlib-result-return-type-lifted ] : https://discuss.kotlinlang.org/t/state-of-kotlin-result-vs-kotlin-result/21103/5
357+ [ stdlib-result-keep ] : https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/result.md#error-handling-style-and-exceptions
358+ [ stdlib-result-runCatching ] : https://github.com/JetBrains/kotlin/blob/v2.2.20/libraries/stdlib/src/kotlin/util/Result.kt#L144
359+ [ ChannelResult ] : https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel-result/
360+ [ CancellationException ] : https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.coroutines.cancellation/-cancellation-exception/
361+ [ rich-hickey-maybe-not ] : https://www.youtube.com/watch?v=YR5WdGrpoug&t=657s
263362
264363[ badge-android ] : http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
265364[ badge-android-native ] : http://img.shields.io/badge/support-[AndroidNative]-6EDB8D.svg?style=flat
@@ -275,3 +374,4 @@ information and licensing terms.
275374[ badge-mac ] : http://img.shields.io/badge/-macos-111111.svg?style=flat
276375[ badge-watchos ] : http://img.shields.io/badge/-watchos-C0C0C0.svg?style=flat
277376[ badge-tvos ] : http://img.shields.io/badge/-tvos-808080.svg?style=flat
377+ [ // ] : # ( @formatter:on )
0 commit comments