KotlinでRustライクなResult型を使う

# 対象読者

Rustの`Result`型（や、他言語における同等機能）を知らない人。
投げる例外しか扱ったことがないのに、kotlin-resultを使ってくれと言われた人。

# 例外を投げる方式の問題点

エラーを通知する方法として、例外を投げるという方法を採る言語は多いです。この方法の欠点は、（言語仕様にもよりますが）エラー発生時のインタフェースを適切に設定できないことです。

例えば、

```kotlin
fun doubleIfPositiveOrThrow(input: Int): Int {
    if (input <= 0) {
        throw Error("Invalid input!")
    }
    return input * 2
}
```

という関数は、引数に整数を取って戻り値として整数を返す、というところまでインタフェース定義できているにも関わらず、例外を投げるかどうかは実装詳細を読む必要があります。このせいで、呼び出し元にエラーハンドリングを強制することができず、バグを生む原因になります。

# エラーを例外として扱わず、戻り値として扱う方法

そこで、いくつかの言語・・・特に新しめの言語では、エラーを例外として特例扱いするのではなく、普通に戻り値として扱う方法を採っています。

例えばGoでは、エラーが発生する可能性のある関数の戻り値は、常に`err`を含みます。

```go
func Atoi(s string) (int, error)
```

この `Atoi`関数の場合、第二戻り値が`error`なので、この関数はエラーが発生する可能性があるのだ、ということが、型を見るだけで分かります。

RustやScalaでは、Goと違ってジェネリクスが使えるため、少し異なるアプローチを採っています。それが`Result`型です。（※Scalaでは`Either`と呼びますが、実質同じです。）

これから紹介するKotlinの`Result`も、それと同じものです。

# kotlin-resultライブラリ

では、本命のkotlin-resultライブラリの説明を始め・・・たいところなのですが、その前に注意点です。

[Kotlinのスタンダードライブラリに`Result`型というのがあります](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/)が、**これは全く異なるもの**です。これはこれでエラーハンドリングに役立ちそうな気配はありますが、今回は触れません。

今回説明するのは、[kotlin-resultライブラリ](https://github.com/michaelbull/kotlin-result)の`Result`型です。

## 関数定義

`Result`を使うと、前述の

```kotlin
fun doubleIfPositiveOrThrow(input: Int): Int {
    if (input <= 0) {
        throw Error("Invalid input!")
    }
    return input * 2
}
```

は、

```kotlin
fun doubleIfPositive(input: Int): Result<Int, String> {
    if (input <= 0) {
        return Err("Invalid input!")
    }
    return Ok(input * 2)
}
```

と書き換えられます。

まずは、インタフェース（関数の型）だけ見てみます。

変更前の`fun doubleIfPositiveOrThrow(input: Int): Int`は、「整数を食って、整数を返す」ということを言っています。ただし実は、定義にないエラーが発生することがあり得ます。

変更後の`fun doubleIfPositive(input: Int): Result<Int, String>`は、「整数を食って、成功したら整数を返す、失敗したら文字列を返す」ということを言っています。（`Result`の第一型引数が成功時の型、第二型引数が失敗時の型を表す。）

関数の中身も見てみましょう。

変更前は`throw`していたところが、変更後は`return Err("Invalid input!")`に変わっています。例外として投げてしまうのではなく、あくまで戻り値として返しているわけです。ただ、エラーであることを表現するため、`Err`で戻り値を囲っています。

最後の`return`ですが、変更前は`return input * 2`とIntを返していたところが、変更後は`return Ok(input * 2)`に変わっています。成功したことを表すため、`Ok`で戻り値を囲うことが必要となります。

## Resultを返す関数の使い方

例えば、

```kotlin
val result = doubleIfPositive(5)
```

とした場合、`result`の値は`10`にはなりません。`Ok(10)`になります。よって、そのままでは使えません。

例えば、以下のように使います。

```kotlin
fun tryResultType() {
    val result = doubleIfPositive(5) // これはOk(10)を返すので、このままではIntとして使えない。

    // 成功なら中身の値(=10)、エラーならnull。
    val doubled0: Int? = result.get()

    // 成功なら中身の値(=10)、エラーなら0。
    val doubled1: Int = result.getOr(0)

    // 成功なら中身の値(=10)、エラーならtryResultType自体から抜けてしまう。
    val doubled2: Int = result.getOrElse { return }
}
```

以上の使用例を見ると分かるように、どの使い方でも、常にエラーだった場合のことを意識しています。つまり、

```kotlin
fun tryResultType() {
    val result = doubleIfPositive(-3) // これはErr("Invalid input!")を返す。

    val doubled0: Int? = result.get() // null

    val doubled1: Int = result.getOr(0) // 0

    val doubled2: Int = result.getOrElse { return } // tryResultType自体から抜けてしまう。
}
```

という感じです。

## いったんまとめ

まとめると、例外を投げる方式と比べて、

- **インタフェース（関数の型）を見るとエラーが発生しうることが分かる。**
- **関数呼び出し側にエラー処理を強制できる。**（→処理忘れによるバグを生みづらい。）

というメリットがあります。

## bindingとbind

しかし、いちいち`getOrElse`などを書くのは結構面倒です。

つまり、従来通りの例外投げ方式だと、以下のように書けていました。

```kotlin
try {
    val a: Int = doubleIfPositiveOrThrow(2)
    val b: Int = doubleIfPositiveOrThrow(5)
    val c: Int = doubleIfPositiveOrThrow(-8)
} catch (e: Error) {
    // エラー処理
}
```

`Result`方式だと、こうなります。

```kotlin
val a: Int = doubleIfPositive(2).getOrElse {
    // エラー処理
    return
}
val b: Int = doubleIfPositive(5).getOrElse {
    // エラー処理
    return
}
val c: Int = doubleIfPositive(-8).getOrElse {
    // エラー処理
    return
}
```

これを楽に書けるようにするのが、`binding`と`bind`です。

`binding`と`bind`を使うと、このように書けます。

```kotlin
binding<Unit, String> {
    val a: Int = doubleIfPositive(2).bind()
    val b: Int = doubleIfPositive(5).bind()
    val c: Int = doubleIfPositive(-8).bind()
}.getOrElse {
    // エラー処理
    return
}
```

簡単に言えば、こういうことです。

`binding`ブロックの中では、`Result`型は`bind()`を呼び出すことができます。
`bind()`は、もし呼び出し元の`Result`が成功(`Ok`)であれば中身の値を取り出し、失敗（`Err`）であれば`binding`ブロックから抜けて、エラー（`Err`）を返します。

楽できるので、使えるところでは`binding`と`bind`を使った方が良いです。

詳細は、[公式ドキュメント](https://github.com/michaelbull/kotlin-result#binding-monad-comprehension)を参照してください。

# 最後に

詳しく説明すると長くなるので、いったんここまでにします。

投げる例外にしか触れてこなかった人にとっては、最初はややこしく感じるかと思います。しかし、投げる例外では、厳密なエラー処理をプロジェクト全体に浸透させるのは難しいです。`Result`を使う方法だと、いい加減に書いてしまうことが許されなくなる分、バグの少ないコードを生み出すことができる・・・と思っています。

少なくとも、Rustは言語レベルでそういう選択をしたわけですし。

とにかく、触っているうちに慣れますから！