StateモナドをIndexed Stateモナドにしていく
通常のMonadとIndexed Monad、
を比べて見る
通常のMonadの定義
hsclass Monad m where
return :: a -> m a
bind :: m a -> (a -> m b) -> m b
Indexed Monadの定義
hsclass IxMonad md where
ireturn :: a -> md i i a
ibind :: md i j a -> (a -> md j k b) -> md i k b
通常のMonadは、IxMonadで表現することができる
IxMonadはMonadの一般化なので。
hsnewtype M m p q a = M { unM:: m a }
instance Monad m => IxMonad (M m) where
ireturn = M . return
ibind (M m) f = M (m >>= unM . f)
通常のState Monadの使用例
状態の初期値に0を取って、内部で1増やしている
hstest1 :: (Int, Int)
test1 = runState c 0
where
c = do
v <- get
put $ succ v
return v
-- (0, 1)
doの糖衣構文を解く
上の test1
をdoを使わずに書く
hstest1' :: (Int, Int)
test1' = runState c 0
where
c = get `bind` (
\v -> put (succ v) `bind` (
\_ -> return v))
-- (0, 1)
Indexed Monadを使って通常のState Monadを使用する
IxMonad
で作ったStateモナド用に、 iget
、 iput
を定義しておく
hsiget :: (MonadState s m) => M m s s s
iget = M get
iput :: (MonadState s m) => s -> M m s s ()
iput = M . put
通常のStateモナドの使用例を、IxMonadを使って書いている
hstest2 :: (Int, Int)
test2 = runState (unM c) 0
where
c = iget `ibind` (
\v -> iput (succ v) `ibind` (
\_ -> ireturn v))
-- (0, 1)
do
なしで書いた test1'
にそっくりであることがわかる
IxMonadは、Monadの一般化なので、
このように通常のStateモナドもIxMonadを使って表現することができる
通常のStateTとmethodの定義
hsnewtype StateT s m v = StateT { runStateT :: s -> m (v,s) }
instance (Monad m) => Monad (StateT s m) where
return x = StateT $ \s -> return (x, s)
bind m f = StateT $ \s -> do
(x, s') <- runStateT m s
runStateT (f x) s'
get :: (Monad m) => StateT s m s
get = state $ \s -> (s, s)
put :: (Monad m) => s -> StateT s m ()
put s = state $ \_ -> ((), s)
bind
の型は、
StateT s m a -> (a -> StateT s m b) -> StateT s m b
actionの実行前後で、Stateの型は変わらず s
である
Indexed Monadを使ってStateTを定義する
hsnewtype IxStateT m si so v = IxStateT { runIxStateT:: si -> m (v, so) }
instance Monad m => IxMonad (IxStateT m) where
ireturn x = IxStateT $ \si -> return (x, si)
ibind (IxStateT m) f = IxStateT $ \si -> do
(x, so) <- m si
runIxStateT (f x) so
iget :: Monad m => IxStateT m si si si
iget = IxStateT (\si -> return (si,si))
iput :: Monad m => so -> IxStateT m si so ()
iput s = IxStateT $ \_ -> return ((), s)
ibind
の型は、
IxStateT m p q a -> (a -> IxStateT m q r b) -> IxStateT m p r b
iget
の型は、 IxStateT m si si si
igetしても、IxStateTが管理する状態は変わらないので、 si
のまま
iput
の型は、 so -> IxStateT m si so ()
so
を iput
することで、 IxStateT
の管理する状態は、 si
から so
に変わる
Indexed State Monadの使用例
test2
を、IxStateTを使って書く
hstest3 :: IO ()
test3 = runIxStateT c 0 >>= print
where
c = iget `ibind` (
\v -> iput (succ v) `ibind` (
\_ -> ireturn v))
-- (0,1)
特に嬉しさもない、ただの比較のためのコード

IxStateの嬉しさの例
状態の初期値に0を取って、内部で1増やして、文字列に変換している
hstest4 :: IO ()
test4 = runIxStateT c 0 >>= print
where
c = iget `ibind` (
\v -> iput (show $ succ v) `ibind` (
\_ -> ireturn v))
-- (0, "1")
RebindableSyntax
拡張を使って、doで見やすく書くと
hs{-# LANGUAGE RebindableSyntax #-}
...
test4' :: IO ()
test4' = runIxStateT c 0 >>= print
where
c = do
v <- iget
iput $ show $ succ v
return v
参考