Applicative と Monad の違いについて


「`Applicative`と`Monad`は何が違うのか？」という疑問についての自分なりのまとめ。

# 型制約の強さの違い

Haskellの型クラスは「特定の名前と型がついた関数が実装されているか」で型をグループ化する概念。言ってみればJavaのinterfaceのようなもので、ある型がその型クラスに属するためにはその型クラスによって指定された名前と型を持つ関数を定義する必要がある。

`Applicative`と`Monad`は型クラスであって、それぞれが要求する関数が異なる。

## 型クラスの定義を読む

GHCi(7.10.2)によると、`Applicative`の定義は以下。

```haskell
Prelude> :i Applicative
class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
  (*>) :: f a -> f b -> f b
  (<*) :: f a -> f b -> f a
```

(ちなみに小文字で書かれた`f`や`a`や`b`は型変数で、ある具体的な型(例えば`IO`とか`Maybe`とか`[]`とか)が入る。同じ型変数には同じ型が代入されるので、`(<*>) :: Maybe (a -> b) -> IO a -> Maybe b`のような代入は起こらない。)

(型の定義に`Hoge a => a`と書かれている場合、「`a`は型クラス`Hoge`に属する」あるいは「`a`は型クラス`Hoge`のインスタンスである」と読める。Javaでいうところの`<T extends Hoge>`のようなもの。)

このApplicativeの定義は次のことを言っている。

次の条件を満たす`f`が`Applicative`になる:

* `f`は`Functor`に属する
* `a -> f a`という型持つ`pure`という関数が実装されている
* `f (a -> b) -> f a -> f b`という型を持つ`<*>`という関数が実装されている
* `f a -> f b -> f b`という型を持つ`*>`という関数が実装されている
* `f a -> f b -> f a`という型を持つ`<*`という関数が実装されている

また、ある型`m`が`Monad`になるためには、以下で定義された関数を実装しないといけない。

```haskell
Prelude> :i Monad
class Applicative m => Monad (m :: * -> *) where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  fail :: String -> m a
```

このMonadの定義は次のことを言っている。

次の条件を満たす`m`が`Monad`になる:

* `m`は`Applicative`に属する
* `m a -> (a -> m b) -> m b`という型持つ`>>=`という関数が実装されている
* `m a -> m b -> m b`という型を持つ`>>`という関数が実装されている
* `a -> m a`という型を持つ`return`という関数が実装されている
* `m a`という型を持つ`fail`という関数が実装されている

`Monad`に属すためには、型クラス`Applicative`に含まれる関数も実装しないといけないし、それに加えて型クラス`Monad`に含まれる関数も実装しないといけない。

型クラスの定義から、「`Monad`に属する型`m`は、`Applicative`に属する型`f`よりも強い制約を持っている」という違いがあることがわかった。




# 「分岐」ができるかどうか

著名なHaskellerであるkazu-yamamotoさんのブログ記事には次のような言及がある。

> 逐次と反復を実現するのが、Applicative 型クラスです。

> 分岐...を実現するのが、Monad です。

http://d.hatena.ne.jp/kazu-yamamoto/20150128/1422413182


> Applicative にできないことは、前のコンテナの計算結果を用いて、後のコンテナが振る舞いを変えることです。別の言い方をすれば、前のコンテナの計算結果を、後のコンテナに渡す方法がありません。これを実現するのが Monad なのです。

http://d.hatena.ne.jp/kazu-yamamoto/20110413/1302683869


ここでの「分岐」とは「前のコンテナの計算結果を用いて、後のコンテナが振る舞いを変える」のことと思われるが、これは一体どういうことだろう。


## `>>=`の有無による違い

`Applicative`と`Monad`の大きな違いに、`>>=`の有無がある。
`Monad`は`>>=`を使えるが、`Applicative`は使えない。
これによって、行うことのできる計算の種類に影響がある。

### `>>=`の型を見る
`>>=`の型宣言は`(>>=) :: m a -> (a -> m b) -> m b`となっている。(ここで`m`は`Monad`に属する。)

引数に`a -> m b`という関数を取ることができるというところがミソで、「`m a`」と「`m a`の実行結果`a`によって次に実行する`m b`を選択する関数`a -> m b`」を`>>=`で結合すると、`m b`が得られる。

```haskell
\l -> if length l > 0 then putStrLn l else putStrLn "(empty)"
```

例えば、こんなふうに型`a`の値(ここでは`l`)によって、次に実行される計算(`putStrLn l`か`putStrLn "(empty)"`か)を切り替えることができる。

これと`getLine`を`>>=`で結合して使うことで、`getLine`を実行した結果によって異なる処理をする`IO ()`が書ける。

```haskell
Prelude> getLine >>= (\l -> if length l > 0 then putStrLn l else putStrLn "(empty)")
hoge
hoge
Prelude> getLine >>= (\l -> if length l > 0 then putStrLn l else putStrLn "(empty)")

(empty)
```


一方、例えばApplicativeの`<*>`の型宣言は`(<*>) :: f (a -> b) -> f a -> f b` で、`a`によって`b`を変えることはできるけど、`f b`を変えることはできない。
例えば、

```haskell
Prelude> (++) <$> getLine <*> getLine
foo
bar
"foobar"
```

こんな感じで`m a`と`m b`を連結させて逐次実行させることはできるけど、`m a`の実行結果`a`に依存して`m b`を変えることはできない。

# 結局何が違うの？

型の持つパワーが違う。

Monadが持つ`>>=`は`a -> m b`という関数を取ることができるため、`m a`を実行して得られる`a`に`m b`が依存するような処理が書ける。Applicativeにはそれが書けない。

ちなみに、Monadを使っていても前の処理の結果に後ろの処理の実行内容が依存しないのであればApplicativeが提供している関数のみでコードを書いたほうが綺麗で安全。(参考:[Applicativeのススメ](http://d.hatena.ne.jp/kazu-yamamoto/20101211/1292021817))

# Disclaimer

筆者も勉強中のため内容については間違いがあるかもしれません。
間違いがある場合、コメント欄にてご指摘いただけると助かります。