Stateモナドが便利に使えた！

最近[HaskellでFlappyBirdを作っている](https://github.com/lotz84/FlappyHaskell)のですが今までどう使っていいかわからなかったStateモナドをうまく使えた気がしたのでqiitaに残しておこうと思います。

Stateモナドは[状態を扱える文脈](http://jutememo.blogspot.jp/2009/10/haskell-state-1.html)とか[ReaderとWriterの随伴から出てくる](http://kagamilove0707.hatenablog.com/entry/2014/11/02/210400)とかそういうやつです。[mtlに入ってるState](https://hackage.haskell.org/package/mtl/docs/Control-Monad-State.html)を使って簡単に説明してみます。

```hs
import Control.Monad.State

add1 :: State Int ()
add1 = do
    n <- get
    put (n + 1)

main = print $ execState add1 0
```

`add1`は単に1を足す関数です。ただしStateモナドを使って実装されていて詳細を見てみると

* 状態から数を取り出して
* 1を足して状態に詰め直す

というようなプログラムになっています。

こんな簡単な例をみても何も良さがわからないと思うので今回自分が出会った便利に使える例を紹介したいです。
FlappyBirdは鳥がふわふわ飛ぶゲームなので鳥を表すオブジェクトが必要になります。

```hs
data Bird = Bird {
            pos   :: (Double, Double)
          , speed :: (Double, Double)
          }
```

キーボードが押されたら上に飛び、そうじゃない時は自由落下するように鳥の位置を更新する関数を考えましょう

```hs
move :: Bool -> Bird -> Bird
move pressed bird = let (posX, posY)     = pos bird
                        (speedX, speedY) = speed bird
                        speedY' = if pressed then 10 else speedY - 0.1
                        posX'   = posX + speedX
                        posY'   = posY + speedY'
                    in  bird {pos = (posX', posY'), speed = (speedX, speedY')}
```

書いてみて思ったのですがまぁ悪くはないですね。これでもいいのですがStateモナドを使うともうちょっとわかりやすくかけます

```hs
getPosX :: State Bird Double
getPosX = get >>= return . fst . pos

setPosX :: Double -> State Bird ()
setPosX x = do
    bird <- get
    let (_, y) = pos bird
    put $ bird {pos = (x, y)}

getPosY :: State Bird Double
getPosY = get >>= return . snd . pos

setPosY :: Double -> State Bird ()
setPosY y = do
    bird <- get
    let (x, _) = pos bird
    put $ bird {pos = (x, y)}

getSpeedX :: State Bird Double
getSpeedX = get >>= return . fst . speed

setSpeedX :: Double -> State Bird ()
setSpeedX x = do
    bird <- get
    let (_, y) = speed bird
    put $ bird {speed = (x, y)}

getSpeedY :: State Bird Double
getSpeedY = get >>= return . snd . speed

setSpeedY :: Double -> State Bird ()
setSpeedY y = do
    bird <- get
    let (x, _) = speed bird
    put $ bird {speed = (x, y)}
```

まずはStateモナドでBirdを操作するための準備コードです。単純なコードですがこれを毎回用意するのは大変ですね… いったん目をつぶって新しい`move'`関数を実装してみましょう。

```hs
move' :: Bool -> State Bird ()
move' pressed = do
    if pressed
        then setSpeedY 10
        else do
            speedY <- getSpeedY
            setSpeedY $ speedY - 0.1
    posX   <- getPosX
    posY   <- getPosY
    speedX <- getSpeedX
    speedY <- getSpeedY
    setPosX $ posX + speedX
    setPosY $ posY + speedY
```

`move`より少し長くなりましたが読みやすくもなりました。

以下のように実行して同じ動作をするか比較してみましょう

```hs
main = do
    let bird = Bird {pos = (0,0), speed = (1,1)}
    print $ move False bird
    print $ execState (move' False) bird
```

Stateを使ったほうが読みやすくはなったわけですが専用の関数を用意するのは大変ですよね。ところで用意した関数は全部getterとsetterでしたよね。HaskellでアクセッサといえばLensですがLensを使うと今までのコードがとても短く簡単に書くことが出来ます。

```hs
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad.State

data Bird = Bird {
            _pos   :: (Double, Double)
          , _speed :: (Double, Double)
          } deriving Show

makeLenses ''Bird

move :: Bool -> State Bird ()
move pressed = do
    if pressed
        then speed._2 .= 10
        else speed._2 -= 0.1
    (speedX, speedY) <- use speed
    pos._1 += speedX
    pos._2 += speedY

main = do
    let bird = Bird {_pos = (0,0), _speed = (1,1)}
    print $ execState (move False) bird
```

どうでしょうか
`lens`ライブラリにはもともとStateで使える演算子が定義してあるので自分で専用の関数を特に用意すること無く使うことができます。複雑なデータ構造の状態を書き換える処理を書くときはStateモナドとLensの組み合わせがとても便利だという話でした :cat2: