具体例から学ぶモナド再入門

# 具体例から学ぶモナド再入門

　Haskellで使うモナドはほんの数種類しかありません。それも3, 4種類に分けられ、同じ種類のモナドは性質も使い方も似ているため、モナドを学ぶならどうせ数種類しかないこれらの具体例を理解するのが近道です。

　実際、ライブラリで定義されているモナドや自分自身で作るモナドは大体`State`モナドか何かのnewtypeですし、`GeneralizedNewtyoeDeriving`拡張のおかげでモナドのインスタンスを自力で書く機会もほとんどありません（本当か？）。

　そこで、一般的な数種類のモナドの紹介をして、それにモナド再入門というタイトルをつけることにしました。

## この記事を読むのに必要な知識

* Haskellの基本的な文法
* モナドの型クラス宣言を見たことがある（`return`と`>>=`の存在を知っている）

## モナドの基礎

　モナドを扱う上で重要な概念は、**アクション**と**実行**の2つです。

* アクション
    * モナドの型を持つ値のこと。`IO ()`や`IO String`や`List Int`など
    * 引数を与えるとアクションになる関数もたくさんある
        *  `putStr :: String -> IO ()`は、引数を与えれば `putStr "Hello" :: IO ()`というアクションになる
* 実行
    * モナドを外すこと。モナドは大抵ただのnewtypeなので、ベースになっている型とモナドを行き来させることができる
    * 通常`run*`という名前を持つ

## 4種類のモナド

　モナドを大きく4つに分類しました。この分類は一般的なものではなく私が考えたものですので、この分類から漏れているモナドもありますし、他の人は違うように分類するかもしれません。

* List系
    * 値を内包するモナド
    * 他の言語にも登場するため、初心者にも馴染み深い
* State系
    * 状態を持つモナド
    * 分かりやすい鬼門
* Cont系
    * 継続モナド
    * これを学ぶのはListやStateの後で良い
* Free系
    * 自由モナド
    * 謎に流行ったことがある

## Listモナド

　任意の数の値を内包するモナドです。

亜種:

* Maybe
* Identity
* Either

　MaybeとIdentityは、Listの保持する値の数を制限したものです。
　EitherはMaybeの一般化ですね。

代表的なアクション:

* Nothing
* [], repeat, ..
* Left

### 特徴

　初めて学ぶモナドは`IO`よりもこちらではないでしょうか？
　この類のモナドは下のような特徴を持ちます。

* コンストラクタが複数あるので、`run*`ができない（Identity除く）
* モナドトランスフォーマー（MaybeT, ListTなど）の使用があまり一般的ではない
* Haskellを知らない人でも`>>=`の挙動に馴染みがある。Javaでは`flatMap`という名前でStreamやOptionalが持っている

　Identityを除いて、これらモナドはしばしば例外処理に使われます。すなわち、例外処理の観点では、アクションが例外を投げることに相当します。
　Identityは何もしないモナドで、モナドトランスフォーマーの文脈でよく使われます。

### 使用例

　`flatMap`っぽい使い方の例。

```Haskell
>>> print $ do
>>>     a <- [1, 2, 3]
>>>     b <- [4, 5, 6]
>>>     return $ a * b
[4,5,6,8,10,12,12,15,18]
```

## Stateモナド

　状態を持つモナドです。

亜種:

* IO
* Reader
* Writer
* `->`
* Kleisli

　Stateは情報を内包するモナドで、アクションによって情報を読み書きすることができます。`IO`は状態として「世界」を持たせたStateモナドで、これはアクションによって「世界」を更新しています。
　Readerは読み込み限定、Writerは追記限定のStateモナドという認識でいいと思います。
　関数`->`も実はモナドであり、引数を状態のように引き回せます。Kleisliは初めて聞くかもしれませんが、この子は`->`の親戚みたいなものです。`->`がHask圏の射を表すのに対して、Kleisliはクライスリ圏の射を表します。

代表的なアクション:

* `get`, `put`, `modify`
* `putStrLn s`, `getLine`, ...
* `ask`, `tell`
* `(*2)`, `(+3)`, ...

### 特徴

　いずれも関数をnewtypeしたものです。
　`m`は任意のモナドを表す型変数。Identityは何もしないモナドなので、この`m`にIdentityを入れると下の`m`のないバージョンを得られます。

* StateT: `s -> m (a, s)`
    * State: `s -> (a, s)`
* ReaderT: `r -> m a`
    * Reader: `r -> a`
* WriterT: `w -> m (a, w)`
    * Writer: `w -> (a, w)`
* `->`: `a -> b`
* Kleisli: `a -> m b`

　StateTとWriterTは同型（直観的には同じ型、もう少し正確にすると情報を落とさずに相互に変換できる型）ですが異なるアクションを持ちます。WriterTのメソッド`tell`は状態に対する追記をすることしかできません。
　ReaderTとKleisliは同型で、Readerと`->`も同型。これらはモナドとしては非常に似た振る舞いをします。

　ところで、関数をnewtypeしたものということは、これらのモナドは小粋な名前のついただけの関数ということです。
　関数のモナド的振る舞いというのはどういうものでしょうか？

　例えば、引数に`Handler`を受け取るような関数があります。

```Haskell
hPutChar :: Handle -> Char -> IO ()
hPutStr :: Handle -> String -> IO ()
hPutStrLn :: Handle -> String -> IO ()
```

　型をよく見ると、出力する文字や文字列を先に与えれば、どれも`Handle -> IO ()`の形にできます。
　ReaderTは`r -> m a`のnewtypeなので、この型にはReaderTをかぶせたり`runReaderT`で元の型に戻したりできます。
　では実際にReaderTをかぶせるとどうなるでしょうか。`mtl`のモジュールを使っています。

```Haskell
import Control.Monad.Reader

type Foo = ReaderT Handle IO

putCharM :: Char -> Foo ()
putCharM = ReaderT . flip hPutChar
-- (2つは省略)
```

　見やすさのために、型シノニムで`ReaderT Handle IO`に`Foo`と名付けました。
　関数`Handle -> IO ()`がアクション`Foo ()`になり、引数からは`Handle`が消えました。もちろん`runReaderT`で元の型に戻ります。
　do文を使って、これらのアクションを繋げてみましょう。

```Haskell
doSomething :: Foo ()
doSomething = do
    putCharM "H"
    putStrM "ello, "
    putStrLnM "monad!"
```

　まるでHandleのない標準出入力用の関数のような使い心地になりました。
　ところで、この`doSomething`も`runReaderT`を使えば元の型に戻ります。

```Haskell
doSomething' :: Handle -> IO ()
doSomething' = runReaderT doSomething
```

　Handleを渡してあげればdoSomethingは`IO ()`という見慣れたアクションになります。ここで渡したHandleは、ReaderTの下敷きになった`hPutChar`などに渡されます。

```Haskell
import System.IO

main :: IO ()
main = do
    handle <- openFile "output.txt" WriteMode
    runReaderT doSomething handle
    hClose handle
```

## Contモナド

　継続モナドです。

代表的なアクション:

* Cont
* callCC
* reset, shift

### 特徴

　ListやStateに比べると、少し難解なモナドです。
　ContTは`(a -> m r) -> m r`という型をモナドのインスタンスにしたもので、これも関数型をnewtypeしたものですね。

　`(a -> m r) -> m r`という型は、リソース管理を要する`with*`関数に多く見られます。代表的なものは`withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r`でしょう。
　この一見奇妙な型は、リソースの獲得/解放をワンセットにしようと思うと生まれます。例えば、使用後は間違いなく閉じなければならない`MyHandle`があったとしたら、下のような関数を提供したくなります。

```Haskell
import Control.Exception (finally)

withMyHandle :: (MyHandle -> IO r) -> IO r
withMyHandle processHandle = do
    handle <- openMyhandle
    processHandle handle
        `finally`
        closeMyHandle handle
```

　この`(MyHandle -> IO r) -> IO r`という型が、まさにContTモナドであるところの`(a -> m r) -> m r`です。

　ファイルハンドルを間違いなく閉じてくれる`withFile`を使って、ファイルをコピーするアクションを書いてみましょう。
　`withFile`を使えば、たとえどこで例外が発生してもファイルハンドルの閉じ忘れが起こりません。そのありがたみは、特に複数のファイルハンドラを同時に開く時に強く感じられます。

```Haskell
import System.IO

copyFile :: ContT () IO ()
copyFile = do
    hSrc <- ContT $ withFile "srcFile.txt" ReadMode
    hDst <- ContT $ withFile "dstFile.txt" WriteMode
    src <- liftIO $ hGetContents hSrc
    liftIO $ hPutStr hDst src
```

　finallyがネストしているのに、インデントは平らなままです。
　もちろん、`runContT`すれば元の継続渡しスタイルに戻ります。`return`で継続渡しスタイルも解除してやれば、綺麗さっぱり`IO ()`が現れます。

```Haskell
copyFile' :: (() -> IO ()) -> IO ()
copyFile' = runContT copyFIle

copyFile'' :: IO ()
copyFile'' = copyFile' return
```

　詳しくは、[継続モナドについて](https://qiita.com/sparklingbaby/items/2eacabb4be93b9b64755)という記事を投稿しているので、そちらを読んでいただけると幸いです。

## Freeモナド

　モナドを作るための枠組みです。

亜種:

* Operational

### 特徴

　モナドを作るための枠組みであり、特徴はベースとなる構造によって与えられます。
　発展的なものなので、詳しい解説はせず、紹介だけさらっと行います。

　Freeは任意の関手からモナドを生み出すことができます。そして、任意の代数的データ型から関手を生み出せるCoyonedaとFreeを組み合わせ、どんな型からでもモナドを作り出せるようにしたものがOperationalです。
　モナドのアクションとは、モナドの型を持つ値のことであると説明しました。つまり、モナドのコンストラクタは全てアクションを生む何かだということです。
　そこで、コンストラクタでアクションを宣言することを考えます。

```Haskell
{-# LANGUAGE GADTs #-}

data ConsoleInst a where
    GetContents :: ConsoleInst String
    PutStr :: String -> ConsoleInst ()
```

　Operationalモナドを使って、これをモナドにします。また、上で宣言したコンストラクタを実際にそのモナドのアクションにします。
　実装にはmonad-skeletonを採用しました。

```Haskell
import Control.Monad.Skeleton

type Console = Skeleton ConsoleInst

getContents' :: Console String
getContents' = bone GetContents

putStr' :: String -> Console ()
putStr' = bone . PutStr
```

　次に、モナドの実行を作成します。ここでどのアクションが何をするのかを定義します。
　ここでは、`GetContents`で`getContents`を、`PutStr`で`putStr`を実行するようにしました。
　見慣れないコードに面食らうかもしれませんが、実際はただのボイラープレートです。

```Haskell
runConsoleIO :: Console a -> IO a
runConsoleIO c = case debone c of
    Return a -> return a
    GetContents :>>= k -> getContents >>= runConsoleIO . k
    PutStr s :>>= k -> putStr s >>= runConsoleIO . k
```

　もう一つ、今度はピュアなバージョンの実行を作成してみましょう。

```Haskell
unConsolePure :: Monad m => Console a -> ReaderT String (WriterT String m) a
unConsolePure c = case debone c of
    Return a -> return a
    GetContents :>>= k -> ask >>= unConsolePure . k
    PutStr s :>>= k -> lift (tell s) >>= unConsolePure . k

runConsolePure :: Monad m => Console a -> String -> m (a, String)
runConsolePure = fmap runWriterT . runReaderT . unConsolePure
```

　同じモナドを実行するのに、異なる複数の結果を得ることができています。インターフェースのようなある種の抽象化ができているということですね。
　このモナドの実行時に各アクションの処理を定義する性質のおかげで、普通は悩みの種であるモックの作成も簡単です。

## まとめ

　ListとStateさえ学べば、Haskellを普通に使うには困らないと思います。
　Contをさらっと使えればスマートに、FreeやOperationalをゴリゴリ使えれば変態になれます。