Haskell/Monad transformers
A Gentle Introduction to Monad Transformers
モナドトランスフォーマー・ステップ・バイ・ステップ
【Control.Monad.Trans】(1) Identityモナド - Qiita
【Control.Monad.Trans】(2) StateTモナド - Qiita
【Control.Monad.Trans】(3) ExceptTモナド - Qiita
【Control.Monad.Trans】(4) ReaderTモナド - Qiita
【Control.Monad.Trans】(5) IOモナド - Qiita
Monad transformersを中心に、モナドの基本的な事項をまとめてみました。(1)~(5)までの連載で、Monad transformersを試行していきます。実際にはそれぞれの回で、Identity, StateT, ExceptT, ReaderT, IO モナドを積み重ねていく例題を示しています。
Monad transformersについては、以下のサイトが表向きは最新といわれるものでしょうが、中身はControl.Monad.Transの方をimportしたりしているので、説明の都合上、Control.Monad.Transを使います。
mtl - Hackage - Haskell
今回はStateTモナドです。
1. StateTモナドの定義
Control.Monad.Trans.State.Strict - Hackage - Haskell
m をbaseモナドとして、以下のようにStateT型を定義します。
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
モナドとしての定義です。baseモナド m のreturnや>>=を使って以下のように定義します。
instance (Monad m) => Monad (StateT s m) where
return a = StateT $ \ s -> return (a, s)
m >>= k = StateT $ \ s -> do
(a, s') <- runStateT m s
runStateT (k a) s'
fail str = StateT $ \ _ -> fail str
この定義からわかるように、m >>= k の m の値は a です。(*1)
failの定義の右辺のfailは、m=Identityの場合は、Identityモナドのfailです。
2. 補助関数 - state, get, put
StateTモナドは通常、グローバルな状態を管理するために使われます。アクセス関数としてgetとputが定義されます。stateは純粋な状態遷移関数をStateTモナドに変換するものです。
-- | Construct a state monad computation from a function.
-- (The inverse of 'runState'.)
state :: (Monad m)
=> (s -> (a, s)) -- ^pure state transformer
-> StateT s m a -- ^equivalent state-passing computation
state f = StateT (return . f)
-- | Fetch the current value of the state within the monad.
get :: (Monad m) => StateT s m s
get = state $ \ s -> (s, s)
-- | @'put' s@ sets the state within the monad to @s@.
put :: (Monad m) => s -> StateT s m ()
put s = state $ \ _ -> ((), s)
3. StateTモナドの使用例
簡単な例を2つ示します。その前に実行コマンドの簡略版を定義しておきます。
runState m = runIdentity . runStateT m
まずはgetとputの使用例です。
import Control.Monad.Trans.State.Strict
import Data.Functor.Identity
-- runState threeTicks 5
tick :: StateT Int Identity Int
tick = do n <- get
put (n+1)
return n
threeTicks :: StateT Int Identity Int
threeTicks = do n1<-tick
n2<-tick
n3<-tick
return(n1+n2+n3)
以下のコマンドで実行します
*Main> runState threeTicks 5
(18,8)
次はStateTでスタックを実現します。stateを明示的に使う例となっています。
Control.Monad.Trans.State.Strict
import Data.Functor.Identity
-- runState tasu [8,7..0]
type Stack = [Int]
tasu :: StateT Stack Identity Int
tasu = do
state $ \xs -> ((), 9:xs) -- push 9
a <- state $ \(x:xs) -> (x, xs) -- a <- pop
b <- state $ \(x:xs) -> (x, xs) -- b <- pop
return (a+b)
ここで注目して欲しいのは、StateTモナドの値である a と b は、それぞれ \(x:xs) -> (x, xs) の x の値であることです。このことは上で述べた(*1)に対応しています。
それでは実行してみましょう。
*Main> runState tasu [8,7..0]
(17,[7,6,5,4,3,2,1,0])
今回は以上となります。