Skip to content

Commit 7d3b71f

Browse files
committed
Add FAQs to README
1 parent 5a25d10 commit 7d3b71f

File tree

1 file changed

+152
-52
lines changed

1 file changed

+152
-52
lines changed

README.md

Lines changed: 152 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
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.
4445
This helps to define a clear happy/unhappy path of execution that is commonly referred to
4546
as [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

8150
Below 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
9463
with [`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

142111
The [`binding`][result-binding] function allows multiple calls that each return a `Result` to be chained imperatively.
112+
143113
When inside a `binding` block, the `bind()` function is accessible on any `Result`. Each call to `bind` will attempt to
144114
unwrap the `Result` and store its value, returning early if any `Result` is an error.
145115

146116
In 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

160124
val 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
194158
eagerly 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

204164
runBlocking {
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

217292
Inspiration 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

239328
Bug reports and pull requests are welcome on [GitHub][github].
@@ -243,6 +332,7 @@ Bug reports and pull requests are welcome on [GitHub][github].
243332
This project is available under the terms of the ISC license. See the [`LICENSE`](LICENSE) file for the copyright
244333
information 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

Comments
 (0)