Haskell语言学习笔记(23)MonadReader, Reader, ReaderT
MonadReader 类型类
class Monad m => MonadReader r m | m -> r where
ask :: m r
ask = reader id
local :: (r -> r) -> m a -> m a
reader :: (r -> a) -> m a
reader f = do
r <- ask
return (f r)
instance Monad m => MonadReader r (ReaderT r m) where
ask = ReaderT.ask
local = ReaderT.local
reader = ReaderT.reader
asks :: MonadReader r m => (r -> a) -> m a
asks = reader
class Monad m => MonadReader r m | m -> r where
MonadReader 是个类型类,它为 ReaderT, RWST 等具有 Reader 功能的 Monad 定义了通用接口。
所谓 Reader 功能是指第一个参数固定的函数,也就是具有固定环境变量的函数。
MonadReader 包含三个函数:ask, local, reader。
ask 获取环境变量 r。
local f m 通过调用函数 f 局部性地修改环境变量 r,然后调用 Monad m 中封装的函数。
reader f 对环境变量 r 调用指定函数 f。
另外同一个模块中还定义了 asks 函数,它与 reader 函数同义。
What's the “|” for in a Haskell class definition?instance Monad m => MonadReader r (ReaderT r m) where
ask = ReaderT.ask
对于 ReaderT 这个Monad转换器来说,ask等函数的定义均由 ReaderT 模块来提供。注意这里点运算符的含义不是函数的合成而是受限名字。
Hackage - Where is the MonadReader implementation for ReaderT defined?
ReaderT Monad转换器
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
instance (Monad m) => Monad (ReaderT r m) where
return = lift . return
m >>= k = ReaderT $ \ r -> do
a <- runReaderT m r
runReaderT (k a) r
instance MonadTrans (ReaderT r) where
lift = liftReaderT
liftReaderT :: m a -> ReaderT r m a
liftReaderT m = ReaderT (const m)
- newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
ReaderT 类型是个 newtype,也就是对现有类型的封装。该类型有三个类型参数:内部 Monad 类型参数 m,共享环境类型参数 r 以及结果类型参数 a。
ReaderT r m a 类型封装了一个共享环境计算函数:\r -> m a,通过 runReaderT 字段可以从 ReaderT 类型中取出这个函数。 - instance (Monad m) => Monad (ReaderT r m) where
如果 m 是个 Monad,那么 ReaderT r m 也是一个 Monad。
对比 Monad 类型类的定义,可知 return 函数的类型签名为:
return :: a -> ReaderT r m a
大致相当于 a -> r -> m a
而 bind 函数的类型签名为:
(>>=) :: ReaderT r m a -> (a -> ReaderT r m b) -> ReaderT r m b
大致相当于 (r -> m a) -> (a -> r -> m b) -> (r -> m b) - return = lift . return
return 函数首先将类型为 a 的值封装进内部 Monad m 中,然后通过 lift 函数将它封装进 ReaderT 这个Monad 转换器之中。
这里左侧的 return 是 ReaderT 这个 Monad 的 return,而右侧的 return 是内部 Monad m 的 return。 - lift = liftReaderT
liftReaderT m = ReaderT (const m)
lift 函数的定义由 liftReaderT 函数提供。
liftReaderT 函数首先将封装在内部 Monad m 中的值封装进常值函数 const 中,然后再将它封装进 ReaderT 这个Monad 转换器之中。
return a
= lift . return $ a
= liftReaderT . return $ a
= ReaderT (const (m a))
= ReaderT $ _ -> m a - m >>= k = ReaderT $ \r -> do
对比函数签名,可知 m 的类型是 ReaderT r m a
而 k 的类型是 a -> Reader r m b - a <- runReaderT m r
runReaderT m 让 m 脱离了 ReaderT 这个 Monad,而 <- 运算符让 runReaderT m r 脱离了内部 Monad m。 - runReaderT (k a) r
k a 的类型是 ReaderT r m b
runReaderT (k a) 让 k a 脱离了 ReaderT 这个 Monad,重新进入内部 Monad m。
证明 ReaderT r m 符合Monad法则:
1. return a >>= f ≡ f a
return a >>= f
≡ (ReaderT $ \_ -> m a) >>= f
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \_ -> m a) r; runReaderT (f a) r}
≡ ReaderT $ \r -> do {a <- (\_ -> m a) r; runReaderT (f a) r}
≡ ReaderT $ \r -> runReaderT (f a) r
≡ ReaderT $ runReaderT (f a)
≡ f a
2. m >>= return ≡ m
m = ReaderT $ \r -> n a
m >>= return
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (return a) r}
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \r -> n a) r; runReaderT (ReaderT $ \_ -> n a) r}
≡ ReaderT $ \r -> do {a <- (\r -> n a) r; (\_ -> n a) r}
≡ ReaderT $ \r -> n a
≡ m
3. (m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
(m >>= f) >>= g
≡ (ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a) r}) >> g
≡ ReaderT $ \r -> do {a <- runReaderT (ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a) r}) r; runReaderT (g a) r}
≡ ReaderT $ \r -> do {a <- (\r -> do {a <- runReaderT m r; runReaderT (f a) r}) r; runReaderT (g a) r}
≡ ReaderT $ \r -> do {a <- do {a <- runReaderT m r; runReaderT (f a) r}; runReaderT (g a) r}
≡ ReaderT $ \r -> (runReaderT m r >>= \a -> runReaderT (f a) r}) >>= \a -> runReaderT (g a) r
m >>= (\x -> f x >>= g)
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT ((\x -> f x >>= g) a) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (f a >>= g) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; runReaderT (ReaderT $ \r -> do {a <- runReaderT (f a) r; runReaderT (g a) r}) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; (\r -> do {a <- runReaderT (f a) r; runReaderT (g a) r}) r}
≡ ReaderT $ \r -> do {a <- runReaderT m r; do {a <- runReaderT (f a) r; runReaderT (g a) r}}
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> runReaderT (f a) r >>= \a -> runReaderT (g a) r)
根据内部 Monad 的法则:(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
ReaderT $ \r -> (runReaderT m r >>= \a -> runReaderT (f a) r}) >>= \a -> runReaderT (g a) r
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> (\a -> runReaderT (f a) r}) a >>= \a -> runReaderT (g a) r)
≡ ReaderT $ \r -> runReaderT m r >>= (\a -> runReaderT (f a) r >>= \a -> runReaderT (g a) r)
证明 ReaderT 中 lift 函数的定义符合 lift 的法则。
1. lift . return ≡ return
lift . return $ a
≡ ReaderT (const (return a))
≡ ReaderT (const (m a))
≡ ReaderT $ \_ -> m a
≡ return a
2. lift (m >>= f) ≡ lift m >>= (lift . f)
假设 m = n a 并且 f a = n b
于是 m >>= f = n b
lift (m >>= f)
≡ ReaderT (const (n b))
≡ ReaderT $ \_ -> n b
lift m >>= (lift . f)
≡ ReaderT (const (n a)) >>= (ReaderT . const . f)
≡ (ReaderT $ \_ -> n a) >>= (\x -> ReaderT . const . f $ x)
≡ ReaderT $ \_ -> runReaderT (ReaderT . const . f $ a) _
≡ ReaderT $ \_ -> runReaderT (ReaderT (const (n b)) _
≡ ReaderT $ \_ -> runReaderT (ReaderT (\_ -> n b) _
≡ ReaderT $ \_ -> n b
ReaderT Monad转换器的函数
ask :: (Monad m) => ReaderT r m r
ask = ReaderT return
local :: (Monad m) => (r -> r) -> ReaderT r m a -> ReaderT r m a
local = withReaderT
reader :: (Monad m) => (r -> a) -> ReaderT r m a
reader f = ReaderT (return . f)
asks :: (Monad m) => (r -> a) -> ReaderT r m a
asks f = ReaderT (return . f)
mapReaderT :: (m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT f m = ReaderT $ f . runReaderT m
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT f m = ReaderT $ runReaderT m . f
withReaderT :: (r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT f m = ReaderT $ runReaderT m . f
withReaderT f m 通过调用函数 f 局部性地修改环境变量 r,然后调用 Monad m 中封装的函数。local :: (Monad m) => (r -> r) -> ReaderT r m a -> ReaderT r m a
local = withReaderT
withReaderT 与 local 的区别在于 withReaderT 能够改变环境变量 r 的类型。
local 不能改变环境变量 r 的类型,它可以被看做 withReaderT 的特例。
Prelude Control.Monad.Reader> runReaderT ask "abc"
"abc"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (withReaderT length ask) "abc"
3
Prelude Control.Monad.Reader> runReaderT (mapReaderT (++ ["def"]) ask) "abc"
["abc","def"]
Prelude Control.Monad.Reader> runReaderT (asks (++ "def")) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ask >> ask) "abc"
"abc"
Prelude Control.Monad.Reader> let ioTask = do {v <- ask; liftIO $ print v}
Prelude Control.Monad.Reader> :t ioTask
ioTask :: (MonadReader a m, MonadIO m, Show a) => m ()
Prelude Control.Monad.Reader> runReaderT ioTask "abc"
"abc"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ioTask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReaderT (local (++ "def") ioTask >> ioTask) "abc"
"abcdef"
"abc"
Reader Monad
type Reader r = ReaderT r Identity
runReader :: Reader r a -> r -> a
runReader m = runIdentity . runReaderT m
mapReader :: (a -> b) -> Reader r a -> Reader r b
mapReader f = mapReaderT (Identity . f . runIdentity)
withReader :: (r' -> r) -> Reader r a -> Reader r' a
withReader = withReaderT
Reader Monad 是 ReaderT Monad(转换器) 的一个特例。
Prelude Control.Monad.Reader> runReader (mapReader (++"def") ask) "abc"
"abcdef"
Prelude Control.Monad.Reader> runReader (withReader (++"def") ask) "abc"
"abcdef"
应用实例
import Control.Monad.Reader
hello :: Reader String String
hello = do
name <- ask
return ("hello, " ++ name ++ "!")
bye :: Reader String String
bye = do
name <- ask
return ("bye, " ++ name ++ "!")
convo :: Reader String String
convo = do
c1 <- hello
c2 <- bye
return $ c1 ++ c2
main = print . runReader convo $ "adit"
import Control.Monad.Reader
hello :: Reader String String
hello = asks $ \name -> ("hello, " ++ name ++ "!")
bye :: Reader String String
bye = asks $ \name -> ("bye, " ++ name ++ "!")
convo :: Reader String String
convo = asks (const (++)) <*> hello <*> bye
main = print . runReader convo $ "adit"
Haskell语言学习笔记(23)MonadReader, Reader, ReaderT的更多相关文章
- Haskell语言学习笔记(88)语言扩展(1)
ExistentialQuantification {-# LANGUAGE ExistentialQuantification #-} 存在类型专用的语言扩展 Haskell语言学习笔记(73)Ex ...
- Haskell语言学习笔记(79)lambda演算
lambda演算 根据维基百科,lambda演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义.函数如何被应用以 ...
- Haskell语言学习笔记(69)Yesod
Yesod Yesod 是一个使用 Haskell 语言的 Web 框架. 安装 Yesod 首先更新 Haskell Platform 到最新版 (Yesod 依赖的库非常多,版本不一致的话很容易安 ...
- Haskell语言学习笔记(20)IORef, STRef
IORef 一个在IO monad中使用变量的类型. 函数 参数 功能 newIORef 值 新建带初值的引用 readIORef 引用 读取引用的值 writeIORef 引用和值 设置引用的值 m ...
- Haskell语言学习笔记(39)Category
Category class Category cat where id :: cat a a (.) :: cat b c -> cat a b -> cat a c instance ...
- Haskell语言学习笔记(37)RWS, RWST
RWST Monad转换器 RWST Monad转换器是将 ReaderT, WriterT 以及 StateT 这三个Monad转换器的功能集于一体的产物. newtype RWST r w s m ...
- Haskell语言学习笔记(30)MonadCont, Cont, ContT
MonadCont 类型类 class Monad m => MonadCont m where callCC :: ((a -> m b) -> m a) -> m a in ...
- Haskell语言学习笔记(25)MonadState, State, StateT
MonadState 类型类 class Monad m => MonadState s m | m -> s where get :: m s get = state (\s -> ...
- Haskell语言学习笔记(24)MonadWriter, Writer, WriterT
MonadWriter 类型类 class (Monoid w, Monad m) => MonadWriter w m | m -> w where writer :: (a,w) -& ...
随机推荐
- 51nod 1965 奇怪的式子——min_25筛
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1965 考虑 \( \prod_{i=1}^{n}\sigma_0^i \) \ ...
- go http 传递json数据
上篇博文中简单介绍了Go HTTP的Server 和Client.本文介绍如何在HTTP中传递json格式的数据. Server package main import ( "encodin ...
- Java技术专题之JVM你的内存泄露了吗?
一.从一个例子开始 关于JVM的内存泄露,让我们从下面一个例子开始吧,大家来判断一下如果按照下面这种分配方式,会不会出现内存泄露呢? import java.util.List; import jav ...
- Qt中路径问题小结
转载:奋斗Andy 在做Qt项目的时候,我们难免遇到到文件路径问题. 如QFile file("text.txt")加载不成功.QPixmap("../text.png& ...
- [转]C#鼠标拖动任意控件
C#鼠标拖动任意控件(winform) 分类: c#2011-08-15 22:51 178人阅读 评论(0) 收藏 举报 winformc#userwindowsobjectapi using Sy ...
- CentOS7 安装supervisor守护进程管理器
supervisor没有发布在标准的CentOS源在,需要安装epel源.这种方式安装的可能不是最新版本,但比较方便,安装完成之后,配置文件会自动帮你生成. 默认配置文件:/etc/superviso ...
- 一种新型远距临场机器人 Fusion / Full Body Surrogacy for Collaborative Communication
近日,来自日本庆应大学的机器人专家开发出一种新型远距临场机器人 Fusion,允许操作者远程控制别人的身体来帮助他们完成操作任务.Fusion「栖居」在代理者的背上,具备立体视觉和双声道听觉,可以通过 ...
- Windows Storage Stack
- Poi对excel的基本操作
1.创建简单excel public static void main(String[] args) throws Exception { Workbook wb=new HSSFWorkbook() ...
- 1.scrapyd部署相关问题
部署scrapy爬虫项目到6800上 启动scrapyd 出现问题 1: scrapyd-deloy -l 未找到相关命令 scrapyd-deploy -l 可以看到当前部署的爬虫项目,但是当我输 ...