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. gitlab使用外部的postgresql、外部的redis服务器

    postgresql创建用户 su - postgres psql create role gitlab login encrypted password 'pass';\du ;显示用户 postg ...

  2. linux mutt的安装和使用

    首先介绍一下mutt这个软件,它是一款基于文字界面的邮件客户端,非常小巧,但功能强大,可以用它来读写,回复保存和删除你的邮件,能在linux命令行模式下收发邮件附件. 我只讲它很小的一部分功能,因为我 ...

  3. HttpFileCollection类

    最近在学HttpRequest类搞文件上传的时候看到Request.Files返回了HttpFileCollection 这个类的一个对象,这个类用于获取浏览器上传的文件集合,在文件上传的时候可以通过 ...

  4. 打开Visual Studio 2012的解决方案 连接 Dynamics CRM 2011 的Connect to Dynamics CRM Server 在其工具下没有显示

    一.使用TFS 代码管理,发现Visual Studio 2012 菜单栏 工具下的Connect to Dynamics CRM Server 没有显示. 平常打开VS下的工具都会出现Connect ...

  5. hosts,No web site is configured at this address.

    解决办法: IIS管理器->右击网站->属性->网站——>IP地址一项->选择全部未分配-> 确定关闭 问题解决.

  6. TS流的解析

    个字节不一定都是有效数据,有一些可能为填充数据). 工作形式: 因为在TS流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据.制定TS流标准的机构就规定了一些数据结构来定义.比如: ...

  7. php函数的实现

    1.函数     汇编中函数对应的是一组独立的汇编指令,然后通过call指令实现函数的调用.PHP编译的opcode数组,与汇编指令对应. PHP用户自定义函数的实现就是将函数编译为独立的opcode ...

  8. 关于Qt配置编译器的问题

    PC系统:win10 安装了:Visual Studio 2017 Community ; Qt 5.8.0 运行Qt程序时,出现以下错误: 原因:来自知乎 只安装了VS2017(MSVC 15.0) ...

  9. [转]C#鼠标拖动任意控件

    C#鼠标拖动任意控件(winform) 分类: c#2011-08-15 22:51 178人阅读 评论(0) 收藏 举报 winformc#userwindowsobjectapi using Sy ...

  10. 解决 pycharm can not save setting

    这个问题出现的原因是因为PyCharm中存在相同名字的虚拟环境变量. 解决方法:Configure Python Interpreter 点击右上角齿轮状按钮, 选择 show all,然后删除相同名 ...