モナドについてSwiftで説明してみた

> モナドって何ですか？
> これをちゃんと説明できる人に会ったことがない。

↑[^1]に挑戦してみます。

まず、モナドには[数学の圏論におけるモナド](https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%8A%E3%83%89_(%E5%9C%8F%E8%AB%96))と[プログラミングにおけるモナド](https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%8A%E3%83%89_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0))があります。圏論のモナドについては僕もよくわかっていません。圏論のモナドとプログラミングのモナドは同じものだというような説明がされていますが、本当に等価なのか僕には判断できません（どうもプログラミングのモナドは圏論のモナドの条件を満たしていそうですが、逆は成り立たないんじゃないかと思っています）。

ここでは、 __プログラミングにおけるモナド__ について説明します。コードはすべて Swift で書きます。モナドについて調べると、よく __モナド則__ について書いてあるんですが、肝心の記述が関数型言語で書かれていてわからん！となることが多いんじゃないかと思います。本投稿では、 __モナド則についても Swift の構文で記述します__。

まず前半でモナドとは何かについて述べ、 __それだけではさっぱり意味がわからないと思うので__、後半でモナドがどのように役立つのかを例を使って説明します。

## モナドとは

`Foo<T>` が次の条件[^2]を満たすとき、 `Foo<T>` はモナドです。

- `init(_ x: T)` というイニシャライザを持つ。
- `func flatMap<U>(f: T -> Foo<U>) -> Foo<U>` というメソッドを持つ。
- 上記の実装が __モナド則__（後述）を満たす。
- `Foo<T>` は __ファンクター__（後述）である。

ざっくりと言えば、適切に実装された（モナド則を満たした） `flatMap` を持った型はモナドと言えるでしょう（モナドはファンクターでもあるので `map` も持っている必要がありますが）。

### モナド則

- `Foo(x).flatMap(f) == f(x)` が成り立つ。
- `foo.flatMap { Foo($0) } == foo` が成り立つ。
- `foo.flatMap { f($0).flatMap(g) } == foo.flatMap(f).flatMap(g)` が成り立つ。

モナド則は、中身をじっくり見てみればわかりますが、ごく当たり前に満たすべき性質を言ってるだけです。言ってみれば、積の結合法則 `(a * b) * c == a * (b * c)` のモナド版のようなものです。

例えば、最初の法則は `x` を `Foo` で包んでからやっぱり中身の `x` を取り出して関数 `f` を適用した結果は、直接 `f` を `x` に適用した場合と等しいという意味です。要するに、イニシャライザで値を包むときや `flatMap` で取り出すときに余計なことをするなというだけです。

積の結合法則を満たさなければ数とは言えないというのと同じように、モナド則を満たさなければモナドとは言えないという当然の性質を述べているに過ぎません。自作の `Number` 型に結合法則が成り立たないような `*` を実装することはできますがそれが意味を持たないのと同じように、モナド則を満たさないモナドっぽい型を実装することはできますがそれはナンセンスなのです。

他の二つについても、中身をじっくり見てみればそれが何を意味するのかわかると思いますし、当然満たすべきものであることがわかると思います。

## ファンクターとは

前述の通り、モナドはファンクターでないといけません。ファンクターとは何でしょうか。

`Foo<T>` が次の条件を満たすとき、 `Foo<T>` はファンクターです。

- `func map<U>(f: T -> U) -> Foo<U>` というメソッドを持つ。
- 上記の実装が __ファンクター則__（後述）を満たす。

同じくざっくりと言えば、適切に実装された `map` を持った型はファンクターと言えるでしょう。

### ファンクター則

- 恒等関数 `id` （後述）に対して、 `foo.map(id) == id(foo)` が成り立つ。
- `foo.map { g(f($0)) } == foo.map(f).map(g)` が成り立つ。

ファンクター則もモナド則同様、じっくり見れば当然満たすべきものだとわかると思います。

#### 恒等関数 id

恒等関数 `id` とは、次のような引数で受けたものをただ返すだけの関数です。

```swift
func id<T>(x: T) -> T {
    return x
}
```

## モナドって何の役に立つの？

ここまで見てきても、モナドがどういうものか、どのように役立つのか、よくわからないと思います。本節では例を使ってモナドを説明します。

まず、僕がモナドについていちばんしっくり来たのは説明を紹介します[^3]。実際にモナドを使っていてもこういう感覚で使います。

> モナドというのは、モナドでくるまれた中の世界ではモナドを気にせずに処理が記述でき、外からみるとモナドでくるまれているという、外と中を分離するための仕組みです。

具体例を見てみましょう。

身近なモナドの例として `Optional` を取り上げます。なお、以下では `Int?` の代わりに `Optional<Int>` と書きますが、これらは等価です。 `Optional<Int>` と書いた方が、前述のモナドの条件と見比べやすいと思うのでそのように書きます。

なお、最低限の知識として、 Swift の `Optional` とクロージャ、高階関数の基本については理解しているものとして説明します（参考： ["SwiftのOptional型を極める"](http://qiita.com/koher/items/c6f446bad54442a28bf4) ）。

### map の例

```swift
let s: String = ...
let n: Optional<Int> = Int(s) // s のパースに失敗したら nil
```

上記のような `n` があったとします。例えば、 `s` が `"123"` なら `n` は `123` ですし、 `s` が `"abc"` なら `n` は `nil` になります。

さて、この `n` を二乗するにはどうすれば良いでしょう？ `n` は `Int` ではなく `Optional<Int>` なので、そのまま `n * n` とは計算できません。答えは↓です。

```swift
// n が数値なら nn は二乗した結果、 n が nil なら nn も nil
let nn: Optional<Int> = n.map { $0 * $0 }
```

これがやっていることは、まさにさっき引用した文章そのままです。 `map` に渡したクロージャ（ `{ $0 * $0 }` ）の中に __「モナドでくるまれた中の世界」__ を作り、そこでは __「モナドを気にせず」__ `Int` として `$0 * $0` が計算できています。一方、 __「外からみると」__ `n` も `nn` も `Optional` という __「モナドでくるまれ」__ たままです。このように、モナドの持つ `map` や `flatMap` が __「外と中を分離する仕組み」__ となり、 __それらを利用することで外と中を分離したまま処理することができるようになる__ わけです[^4]。

### flatMap の例

`map` で「中の世界」を作れればそれだけで十分じゃないかと思ってしまいますが、 `flatMap` がなければうまくいかないケースもあります。

["SwiftのOptional型を極める"](http://qiita.com/koher/items/c6f446bad54442a28bf4) の「力試し」の問題 3, 4 の解答がそれに当たるので、そちらを御覧下さい。実は上記の `map` の例も「力試し」の問題 1 と同じです。

### Optional 以外の例

`Optional` だけではわかりづらいところもあるので、過去の投稿の中から、他のモナドについて述べたものを挙げておきます。

- `Either`: [Swift 3.0で追加されそうなEitherについて](http://qiita.com/koher/items/288f01cddb0bbb6aa4e1)
- `Array`: [ArrayとOptionalのmapは同じです](http://qiita.com/koher/items/68585cff691d87124077), ["Swift 2.0の新しいflatMapが便利過ぎる"](http://qiita.com/koher/items/6a95cb8edb04fb81f787)
- `Promise`: [koher/PromiseK](https://github.com/koher/PromiseK/), ["Promise と比べてみる"](http://qiita.com/koher/items/c6f446bad54442a28bf4#promise-%E3%81%A8%E6%AF%94%E3%81%B9%E3%81%A6%E3%81%BF%E3%82%8B)

## まとめ

ざっくり言うと、モナドとは `map` と `flatMap` を持った型で、それらがファンクター則、モナド則を満たしている必要があります。

`map` や `flatMap` を使うと、モナドに包まれた中の世界とモナドの外の世界を分離したまま処理を記述することができ、いちいち包まれた値を取り出したり包みなおしたりしなくていいので便利です。

-----------------

[^1]: @syou007 さん、["プログラマー初学の人への質問"](http://qiita.com/syou007/items/3e0c14512ee29659ec82) より。
[^2]: __十分条件__ です。 __必要条件__ ではありません。つまり、この条件を満たせばモナドと言えますが、これでなければならないわけではありません。他の表現の仕方もあります。そのため、「定義」という言葉は使っていません。例えば、 `map` や `flatMap` という名前でなければならないわけではありませんし（ `flatMap` は `bind` と呼ばれることも多いです）、 `map` や `flatMap` がメソッドでなくグローバル関数だとしてもモナドと言えます。 Haskell では `map` は `(T -> U) -> Foo<T> -> Foo<U>` という型の関数です。他にも、 `flatMap` の代わりに `func flatten<T>(foo: Foo<Foo<T>>) -> Foo<T>` という関数を実装しても OK です。その場合、 `flatMap` は `flatten(foo.map(f))` によって実現されます。詳しくは知りませんが、圏論では `flatMap` ではなく `flatten` 相当のものによって表現するのが一般的なようです。
[^3]: ["Java8でのプログラムの構造を変えるOptional、ただしモナドではない - きしだのはてな"](http://d.hatena.ne.jp/nowokay/20130524) より。
[^4]: ただし、ここまでは `map` しか使っていないのでモナドでなくてもファンクターで十分な話です。