【Control.Monad.Trans】(4) ReaderTモナド

[Haskell/Monad transformers](https://en.wikibooks.org/wiki/Haskell/Monad_transformers)
[A Gentle Introduction to Monad Transformers](https://github.com/kqr/gists/blob/master/articles/gentle-introduction-monad-transformers.md)
[モナドトランスフォーマー・ステップ・バイ・ステップ](http://bicycle1885.hatenablog.com/entry/2012/12/08/165236)

---

[【Control.Monad.Trans】(1) Identityモナド - Qiita](https://qiita.com/sand/items/e88b798d8783606071c5)
[【Control.Monad.Trans】(2) StateTモナド - Qiita](https://qiita.com/sand/items/802b8c4a8ae19f04102b)
[【Control.Monad.Trans】(3) ExceptTモナド - Qiita](https://qiita.com/sand/items/fe3c29560136803e8b11)
[【Control.Monad.Trans】(4) ReaderTモナド - Qiita](https://qiita.com/sand/items/93d6ba75b0c4e39ce295)
[【Control.Monad.Trans】(5) IOモナド - Qiita](https://qiita.com/sand/items/ee3c19c191c87c32f867)


Monad transformersを中心に、モナドの基本的な事項をまとめてみました。(1)～(5)までの連載で、Monad transformersを試行していきます。実際にはそれぞれの回で、Identity, StateT, ExceptT, ReaderT, IO モナドを積み重ねていく例題を示しています。


Monad transformersについては、以下のサイトが表向きは最新といわれるものでしょうが、中身はControl.Monad.Transの方をimportしたりしているので、説明の都合上、Control.Monad.Transを使います。
[mtl - Hackage - Haskell](https://hackage.haskell.org/package/mtl)


今回はReaderTモナドです。


# 1. ReaderTモナドの定義

[Control.Monad.Trans.Reader - Hackage - Haskell](https://hackage.haskell.org/package/transformers-0.5.6.2/docs/Control-Monad-Trans-Reader.html)

ReaderTモナドは設定ファイルを読み込むような感じで、環境変数を受け継いでいく機能を提供します。

型は以下の通りです

```Haskell:
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
````

モナドの定義は以下の通りです。

```
instance (Monad m) => Monad (ReaderT r m) where
    return   = lift . return   -- 右辺のreturn :: r -> m r
    m >>= k  = ReaderT $ \ r -> do
        a <- runReaderT m r
        runReaderT (k a) r
    (>>) = (*>)
    m >> k = ReaderT $ \ r -> runReaderT m r >> runReaderT k r
    fail msg = lift (fail msg)
```




# 2. 補助関数 - lift, ask

lift (liftReaderT) は m a を ReaderT r m a に持ち上げるための関数です。
ask は現在の環境の値を取得します。

```Haskell:
-- | Fetch the value of the environment.
ask :: (Monad m) => ReaderT r m r
ask = ReaderT return

instance MonadTrans (ReaderT r) where
    lift   = liftReaderT

liftReaderT :: m a -> ReaderT r m a
liftReaderT m = ReaderT (const m)     -- (const m) :: r -> m a
```




#3. ReaderTの使用例  -  ExceptTとStateTの上に乗っけてみる

以下の記事の例を拡張します。
[【Control.Monad.Trans】(3) ExceptTモナド - Qiita](https://qiita.com/sand/items/fe3c29560136803e8b11)


つまり ReaderT r m a　モナドの m に ExceptT を使います。環境変数の値として掛け算か足し算を表す '*' or '+' を渡すようにします。　


```Haskell:
import Data.Functor.Identity
import Control.Monad
import Control.Monad.Trans.State.Strict
import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader

--  runState (runExceptT (runReaderT mainCalc '*') ) []
--  runState (runExceptT (runReaderT mainCalc '+') ) [8]
--  runState (runExceptT (runReaderT mainCalc '*') ) [8,7]
--  runState (runExceptT (runReaderT mainCalc '+') ) [8,7..0]


type Stack = [Int]

lift :: Monad m => m a -> ExceptT e m a
lift = ExceptT . liftM Right

liftReaderT :: m a -> ReaderT r m a
liftReaderT m = ReaderT (const m)

calc :: ReaderT Char (ExceptT String (StateT Stack Identity)) Int
calc = do
  s <- liftReaderT $ lift $ get
  case s of
    []  -> liftReaderT $ throwE "Error: Stack is empty !"
    [_] -> liftReaderT $ throwE "Error: Stack has only one element !"
    _  -> do 
          a <- liftReaderT $ lift $ state $ \(x:xs) -> (x, xs) -- a <- pop
          b <- liftReaderT $ lift $ state $ \(x:xs) -> (x, xs) -- b <- pop
          op <- ask
          case op of
            '*' -> return (a*b) 
            '+' -> return (a+b)
            _   -> return 0

mainCalc :: ReaderT Char (ExceptT String (StateT Stack Identity)) Int
mainCalc = do
  a <- calc    -- pointA
  liftReaderT $ lift $ state $ \xs -> ((), a:xs)       -- push a
  return a
```

以下が実行例です。ReaderTによる環境変数の受け渡しも、ExceptTのエラー処理も、StateTの状態管理も思い通り動作しているのがわかります。

```
*Main> runState (runExceptT (runReaderT mainCalc '*') ) []
(Left "Error: Stack is empty !",[])
*Main> runState (runExceptT (runReaderT mainCalc '+') ) [8]
(Left "Error: Stack has only one element !",[8])
*Main> runState (runExceptT (runReaderT mainCalc '*') ) [8,7]
(Right 56,[56])
*Main> runState (runExceptT (runReaderT mainCalc '+') ) [8,7..0]
(Right 15,[15,6,5,4,3,2,1,0])
*Main> runState (runExceptT (runReaderT mainCalc '*') ) [8,7..0]
(Right 56,[56,6,5,4,3,2,1,0])
```


ReaderTとStateTの引数は、それぞれのrunレベルにおいて与えていることに注意してください。Monad transformersはそれぞれのモナドが、綺麗に積み重なって実行されていると言えるでしょう。


今回は以上です。