MonadState 类型类

class Monad m => MonadState s m | m -> s where
get :: m s
get = state (\s -> (s, s)) put :: s -> m ()
put s = state (\_ -> ((), s)) state :: (s -> (a, s)) -> m a
state f = do
s <- get
let ~(a, s') = f s
put s'
return a modify :: MonadState s m => (s -> s) -> m ()
modify f = state (\s -> ((), f s)) modify' :: MonadState s m => (s -> s) -> m ()
modify' f = state (\s -> let s' = f s in s' `seq` ((), s')) gets :: MonadState s m => (s -> a) -> m a
gets f = do
s <- get
return (f s) instance Monad m => MonadState s (Lazy.StateT s m) where
get = Lazy.get
put = Lazy.put
state = Lazy.state instance Monad m => MonadState s (Strict.StateT s m) where
get = Strict.get
put = Strict.put
state = Strict.state
  • class Monad m => MonadState s m | m -> s where

    MonadState 是个类型类,它为 StateT, RWST 等具有 State 功能的 Monad 定义了通用接口。

    所谓 State 功能是指对状态计算环境的封装,也就是对函数 \s -> (a, s) 的封装。

    MonadWriter 包含三个函数:get, put, state。

    get 将结果值设置为状态值 s,状态值 s 保持不变。

    put s 将结果值设为空,将状态值设为 s。

    state f 将函数 f 封装进 Monad。

    另外同一个模块中还定义了 modify 和 gets 函数。

    modify f 将结果值设为空,将状态值设为 f s。

    gets f 将结果值设为 f s,状态值 s 保持不变。

    What's the “|” for in a Haskell class definition?

  • instance Monad m => MonadState s (Lazy.StateT s m) where

    get = Lazy.get

    对于 StateT 这个Monad转换器来说,get等函数的定义均由 StateT 模块来提供。注意这里点运算符的含义不是函数的合成而是受限名字。

    Hackage - Where is the MonadReader implementation for ReaderT defined?

StateT Monad转换器

newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }

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'
  • newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }

    StateT 类型是个 newtype,也就是对现有类型的封装。该类型有三个类型参数:内部 Monad 类型参数 m,状态类型参数 s 以及结果类型参数 a。

    StateT s m 类型封装了一个状态转换函数:\s -> m (a,s’),通过 runStateT 字段可以从 StateT 类型中取出这个函数。

    该函数接收一个状态参数 s,经过计算(转换)之后返回一对封装在内部 Monad m 中的值:计算结果 a 以及新的状态 s'。
  • instance (Monad m) => Monad (StateT s m) where

    如果 m 是个 Monad,那么 StateT s m 也是一个 Monad。

    对比 Monad 类型类的定义,可知 return 函数的类型签名为:

    return :: a -> StateT s m a

    大致相当于 a -> s -> m (a,s)

    而 bind 函数的类型签名为:

    (>>=) :: StateT s m a -> (a -> StateT s m b) -> StateT s m b

    大致相当于 (s -> m (a,s)) -> (a -> s -> m (b,s))) -> (s -> m (b,s))
  • return a = StateT $ \s -> return (a, s)

    return 函数将 a 封装进了状态转换函数,该函数首先把结果值设为 a,状态值 s 保持不变,然后把这对值封装进了内部 Monad m。

    这里左侧的 return 是 StateT 这个 Monad 的 return,而右侧的 return 是内部 Monad m 的 return。
  • m >>= k = StateT $ \s -> do

    对比函数签名,可知 m 的类型为 StateT s m a,大致相当于 s -> m (a,s)。

    而 k 的类型为 a -> StateT s m b,大致相当于 a -> s -> m (b,s)

    bind 操作符组合两个状态转换函数,最终结果仍然是个状态转换函数。
  • ~(a, s') <- runStateT m s

    这里首先利用 runState 字段取出 StateT Monad m 中封装的状态转换函数,然后将它应用于状态值 s 之上,得到结果值 a 以及新的状态值 s'。

    runStateT m 让 m 脱离了 StateT 这个 Monad,而 <- 运算符让 runStateT m s 脱离了内部 Monad m。
  • runStateT (k a) s'

    根据 k 的类型 a -> StateT s m b,可知 k a 的类型为 StateT s m b,即 k a 也是一个 StateT Monad。

    这里首先利用 runStateT 字段取出 StateT Monad (k a) 中封装的状态转换函数,然后将它应用于状态值 s' 之上,得到一对封装在内部 Monad m 中的值:最终结果值 a' 和最终状态值 s''。
证明 StateT s m 符合Monad法则:
1. return a >>= f ≡ f a
return a >>= f
≡ (StateT $ \s -> return (a, s)) >>= f
≡ StateT (\s -> m (a, s)) >>= f
≡ StateT $ \s -> do {~(a, s') <- runStateT (StateT (\s -> m (a, s))) s; runStateT (f a) s'}
≡ StateT $ \s -> do {~(a, s') <- m (a, s); runStateT (f a) s'}
≡ StateT $ \s -> do {~(a, s') <- m (a, s); runStateT (f a) s'}
≡ StateT $ \s -> runStateT (f a) s
≡ StateT $ runStateT (f a)
≡ f a
2. m >>= return ≡ m
m = StateT (\s -> m (a, s))
m >>= return
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT (return a) s'}
≡ StateT $ \s -> do {~(a, s') <- runStateT (StateT (\s -> m (a, s))) s; runStateT (StateT (\s -> m (a, s))) s'}
≡ StateT $ \s -> do {~(a, s') <- (\s -> m (a, s)) s; (\s -> m (a, s)) s'}
≡ StateT $ \s -> do {~(a, s') <- m (a, s); m (a, s')}
≡ StateT $ \s -> m (a, s)
≡ m
3. (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
(m >>= f) >>= g
≡ (StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT (k a) s'}) >> g
≡ StateT $ \s -> do {~(a, s') <- runStateT (StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT (f a) s'}) s; runStateT (g a) s'}
≡ StateT $ \s -> do {~(a, s') <- (\s -> do {~(a, s') <- runStateT m s; runStateT (f a) s'}) s; runStateT (g a) s'}
≡ StateT $ \s -> do {~(a, s') <- do {~(a, s') <- runStateT m s; runStateT (f a) s'}); runStateT (g a) s'}
≡ StateT $ \s -> (runStateT m s >>= \(a, s') -> runStateT (f a) s') >>= \(a, s') -> runStateT (g a) s'
m >>= (\x -> f x >>= g)
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT ((\x -> f x >>= g) a) s'}
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT (f a >>= g) s'}
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; runStateT (StateT $ \s -> do {~(a, s') <- runStateT (f a) s; runStateT (g a) s'}) s'}
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; (\s -> do {~(a, s') <- runStateT (f a) s; runStateT (g a) s'}) s'}
≡ StateT $ \s -> do {~(a, s') <- runStateT m s; do {~(a, s') <- runStateT (f a) s'; runStateT (g a) s'}}
≡ StateT $ \s -> runStateT m s >>= \(a, s') -> (runStateT (f a) s' >>= \(a, s') -> runStateT (g a) s')
根据内部 Monad 的法则:(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
StateT $ \s -> (runStateT m s >>= \(a, s') -> runStateT (f a) s') >>= \(a, s') -> runStateT (g a) s'
≡ StateT $ \s -> runStateT m s >>= (\(a, s') -> (\(a, s') -> runStateT (f a) s')) (a, s') >>= \(a, s') -> runStateT (g a) s')
≡ StateT $ \s -> runStateT m s >>= \(a, s') -> (runStateT (f a) s' >>= \(a, s') -> runStateT (g a) s')

lift 函数

instance MonadTrans (StateT s) where
lift m = StateT $ \ s -> do
a <- m
return (a, s)
证明 StateT 中 lift 函数的定义符合 lift 的法则。
1. lift . return ≡ return
lift . return $ a
≡ lift (m a)
≡ StateT $ \s -> do {a <- m a; return (a, s)}
≡ StateT $ \s -> m (a, s)
≡ return a
2. lift (m >>= f) ≡ lift m >>= (lift . f)
假设 m = n a 并且 f a = n b
于是 m >>= f = n b
lift (m >>= f)
≡ lift (n b)
≡ StateT $ \s -> do {a <- n b; return (a, s)}
≡ StateT $ \s -> n (b, s)
lift m >>= (lift . f)
≡ (StateT $ \s -> do {a <- n a; return (a, s)}) >>= (\x -> StateT $ \s -> do {a <- f x; return (a, s)})
≡ (StateT $ \s -> n (a s)) >>= (\x -> StateT $ \s -> do {a <- f x; return (a, s)})
≡ StateT $ \s -> do {runStateT (StateT $ \s -> do {a <- f a; return (a, s)}) s}
≡ StateT $ \s -> do {runStateT (StateT $ \s -> do {a <- n b; return (a, s)}) s}
≡ StateT $ \s -> do {runStateT (StateT $ \s -> n (b s)) s}
≡ StateT $ \s -> n (b s)

StateT 是 Functor 也是 Applicative

instance (Functor m) => Functor (StateT s m) where
fmap f m = StateT $ \ s ->
fmap (\ ~(a, s') -> (f a, s')) $ runStateT m s instance (Functor m, Monad m) => Applicative (StateT s m) where
pure a = StateT $ \ s -> return (a, s)
StateT mf <*> StateT mx = StateT $ \ s -> do
~(f, s') <- mf s
~(x, s'') <- mx s'
return (f x, s'')
m *> k = m >>= \_ -> k

StateT 是 Alternative 也是 MonadPlus

instance (Functor m, MonadPlus m) => Alternative (StateT s m) where
empty = StateT $ \ _ -> mzero
StateT m <|> StateT n = StateT $ \ s -> m s `mplus` n s instance (MonadPlus m) => MonadPlus (StateT s m) where
mzero = StateT $ \ _ -> mzero
StateT m `mplus` StateT n = StateT $ \ s -> m s `mplus` n s

StateT Monad转换器的函数

get :: (Monad m) => StateT s m s
get = state $ \ s -> (s, s) put :: (Monad m) => s -> StateT s m ()
put s = state $ \ _ -> ((), s) modify :: (Monad m) => (s -> s) -> StateT s m ()
modify f = state $ \ s -> ((), f s) modify' :: (Monad m) => (s -> s) -> StateT s m ()
modify' f = do
s <- get
put $! f s gets :: (Monad m) => (s -> a) -> StateT s m a
gets f = state $ \ s -> (f s, s) state :: (Monad m) => (s -> (a, s)) -> StateT s m a
state f = StateT (return . f) evalStateT :: (Monad m) => StateT s m a -> s -> m a
evalStateT m s = do
~(a, _) <- runStateT m s
return a execStateT :: (Monad m) => StateT s m a -> s -> m s
execStateT m s = do
~(_, s') <- runStateT m s
return s' mapStateT :: (m (a, s) -> n (b, s)) -> StateT s m a -> StateT s n b
mapStateT f m = StateT $ f . runStateT m withStateT :: (s -> s) -> StateT s m a -> StateT s m a
withStateT f m = StateT $ runStateT m . f

evalStateT m s 针对 State Monad m 利用初始状态值 s 进行状态计算,然后返回最终结果值 a'。

execStateT m s 针对 State Monad m 利用初始状态值 s 进行状态计算,然后返回最终状态值 s'。

mapStateT f m 针对 State Monad m 进行状态计算之后,对最终结果值和状态值调用函数 f。

withStateT f m 针对 State Monad m 进行状态计算之前,对初始状态值调用函数 f。

Prelude Control.Monad.State> runStateT (return 15) 1
(15,1)
Prelude Control.Monad.State> runStateT get 1
(1,1)
Prelude Control.Monad.State> runStateT (put 3) 1
((),3)
Prelude Control.Monad.State> runStateT (modify (+1)) 1
((),2)
Prelude Control.Monad.State> runStateT (gets (+1)) 1
(2,1)
Prelude Control.Monad.State> evalStateT (gets (+1)) 1
2
Prelude Control.Monad.State> execStateT (gets (+1)) 1
1
Prelude Control.Monad.State> runStateT (do put 3; return 15) 1
(15,3)
Prelude Control.Monad.State> runStateT (put 3 >> return 15) 1
(15,3)

State Monad

type State s = StateT s Identity

runState :: State s a -> s -> (a, s)
runState m = runIdentity . runStateT m evalState :: State s a -> s -> a
evalState m s = fst (runState m s) execState :: State s a -> s -> s
execState m s = snd (runState m s) mapState :: ((a, s) -> (b, s)) -> State s a -> State s b
mapState f = mapStateT (Identity . f . runIdentity) withState :: (s -> s) -> State s a -> State s a
withState = withStateT

State Monad 是 StateT Monad(转换器) 的一个特例。

理解 State Monad

假设存在以下的State Monad 的实例 f

f :: State s a

我们可以把 f 这个 State Monad 粗略地理解为一个参数类型为 s 返回值类型为 a 的普通函数。

在 f 这个函数之中,“参数” s 可以

  • 通过调用 modify 或 gets 函数将其隐式地传给其他函数。
  • 使用 get 函数读取它的值,使用 put 函数设置它的值,使用 modify 函数修改它的值。
  • 通过调用 get 或 gets 函数将其反映为“返回值” a。

在 f 这个函数之中,“返回值” a 可以

  • 通过调用 get 或 gets 函数间接设置它的值。
  • 通过调用 return 函数直接设置它的值。
  • 通过调用 modify 或 put 函数被清空。

Why must we use state monad instead of passing state directly?

应用实例

import Control.Monad.State

type Stack = [Int]

pop :: State Stack Int
pop = state $ \(x:xs) -> (x,xs) push :: Int -> State Stack ()
push a = state $ \xs -> ((),a:xs) stackManip :: State Stack Int
stackManip = do
push 3
a <- pop
pop stackStuff :: State Stack ()
stackStuff = do
a <- pop
if a == 5
then push 5
else do
push 3
push 8 moreStack :: State Stack ()
moreStack = do
a <- stackManip
if a == 100
then stackStuff
else return () stackyStack :: State Stack ()
stackyStack = do
stackNow <- get
if stackNow == [1,2,3]
then put [8,3,1]
else put [9,2,1] main = do
print $ runState stackManip [5,8,2,1]
print $ runState stackStuff [9,0,2,1,0]
print $ runState stackyStack [9,0,2,1,0] {-
(5,[8,2,1])
((),[8,3,0,2,1,0])
((),[9,2,1])
-}

Haskell语言学习笔记(25)MonadState, State, StateT的更多相关文章

  1. Haskell语言学习笔记(88)语言扩展(1)

    ExistentialQuantification {-# LANGUAGE ExistentialQuantification #-} 存在类型专用的语言扩展 Haskell语言学习笔记(73)Ex ...

  2. Haskell语言学习笔记(79)lambda演算

    lambda演算 根据维基百科,lambda演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义.函数如何被应用以 ...

  3. Haskell语言学习笔记(69)Yesod

    Yesod Yesod 是一个使用 Haskell 语言的 Web 框架. 安装 Yesod 首先更新 Haskell Platform 到最新版 (Yesod 依赖的库非常多,版本不一致的话很容易安 ...

  4. Haskell语言学习笔记(20)IORef, STRef

    IORef 一个在IO monad中使用变量的类型. 函数 参数 功能 newIORef 值 新建带初值的引用 readIORef 引用 读取引用的值 writeIORef 引用和值 设置引用的值 m ...

  5. Haskell语言学习笔记(39)Category

    Category class Category cat where id :: cat a a (.) :: cat b c -> cat a b -> cat a c instance ...

  6. Haskell语言学习笔记(44)Lens(2)

    自定义 Lens 和 Isos -- Some of the examples in this chapter require a few GHC extensions: -- TemplateHas ...

  7. Haskell语言学习笔记(38)Lens(1)

    Lens Lens是一个接近语言级别的库,使用它可以方便的读取,设置,修改一个大的数据结构中某一部分的值. view, over, set Prelude> :m +Control.Lens P ...

  8. Haskell语言学习笔记(30)MonadCont, Cont, ContT

    MonadCont 类型类 class Monad m => MonadCont m where callCC :: ((a -> m b) -> m a) -> m a in ...

  9. Haskell语言学习笔记(72)Free Monad

    安装 free 包 $ cabal install free Installed free-5.0.2 Free Monad data Free f a = Pure a | Free (f (Fre ...

随机推荐

  1. base64编码的原理及实现

    base64编码的原理及实现 我们的图片大部分都是可以转换成base64编码的data:image. 这个在将canvas保存为img的时候尤其有用.虽然除ie外,大部分现代浏览器都已经支持原生的基于 ...

  2. LOJ 164 【清华集训2015】V——线段树维护历史最值

    题目:http://uoj.ac/problem/164 把操作改成形如 ( a,b ) 表示加上 a 之后对 b 取 max 的意思. 每个点维护当前的 a , b ,还有历史最大的 a , b 即 ...

  3. 51nod 1965 奇怪的式子——min_25筛

    题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1965 考虑 \( \prod_{i=1}^{n}\sigma_0^i \) \ ...

  4. Dynamics CRM 2011 怎么根据记录的etc参数值找到实体英文名和根据etc参数值或英文名称找到其实体中文名称

    一.平常我们可以打开CRM2011一条已创建的记录,通过JScript方法获取实体英文名的方法是:按F12,输入contentIFrame.Xrm.Page.data.entity.getEntity ...

  5. Mybatis常见面试题 二

    1.mybatis是什么?  (1)mybatis是一个优秀的基于java的持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动.创建连接.创建state ...

  6. UI设计心得

    旁观型ui.追求一种无所不在,同时低调退隐的,奢华的存在感.内容由用户自己去搜索,浏览,构建,召唤,或是随着信息世界的某种外界趋势自然产生,ui作为始终凌驾于用户之上的高高在上的守护神,随时起到中承, ...

  7. Bootstrap:教程、简介、环境安装

    ylbtech-Bootstrap:教程.简介.环境安装 1. Bootstrap 教程返回顶部 1. Bootstrap 教程 Bootstrap,来自 Twitter,是目前最受欢迎的前端框架.B ...

  8. override的实现原理

    转载 http://blog.csdn.net/fan2012huan/article/details/51007517 基于基类的调用和基于接口的调用,从性能上来讲,基于基类的调用性能更高 .因为i ...

  9. Storm集成Kafka的Trident实现

      原本打算将storm直接与flume直连,发现相应组件支持比较弱,topology任务对应的supervisor也不一定在哪个节点上,只能采用统一的分布式消息服务Kafka.   原本打算将结构设 ...

  10. 【Unix网络编程】chapter1简介

    1.1 概述 1.2一个简单的时间获取客户程序 网际套接字地址结构中IP地址和端口号这两个成员必须使用特定的格式,为此我们调用库函数htons("主机到网络端整数")去转换二进制端 ...