函数本地 haskell Reader monad
Function local haskell Reader monad
我考虑下面两个表达式是否等价:
env <- ask
local (\_ -> env) sth
和:
local (\env -> env) sth
如果不是,参数 lambda 被带到哪里?
不太确定你的问题是什么,但让我们试一试。
所以你问的是
ask >>= \env -> local (const env) sth
和
local id sth
是等价的。
就效果而言 -- 当然是。更重要的是,它们都等同于 sth
。让我们考虑一个稍微复杂一点的例子:
ask >>= \env -> local (const (f env)) sth
对
local f sth
现在让我们试着了解它是如何工作的。
local
定义为
local :: (r -> r) -> Reader r a -> Reader r a
local f m = Reader $ runReader m . f
(我简化了一点,因为它实际上是通过 withReaderT
为 ReaderT
定义的,但它理解了这个想法)
提醒一下,我们可以假设
runReader :: Reader r a -> r -> a
此外,提醒一下 Reader r a
是一种包装 r -> a
.
的新类型
现在,>>=
在这里做什么?简单,
m >>= k = Reader $ \r -> runReader (k (runReader m r)) r
因此,它需要 m
,从中提取值,并将其传递给 k
,同时将实际环境 r
作为自由参数。作为一个行为良好的 monadic bind 应该,所以这里不足为奇。
还要注意,ask
只是 Reader id
现在,让我们完全放弃整个 Reader
东西,将我们的表达式重写为函数。
ask >>= \env -> local (const (f env)) sth
然后变成
\r -> (\r' -> runReader sth (const (f r) r')) r
和
local f sth
变成
\r -> runReader sth (f r)
现在稍微眯一下,就可以看出这两者在效果上是等价的。的确,
const (f r) r' = f r
(\r' -> runReader sth (f r)) r = (\_ -> runReader sth (f r)) r
= runReader sth (f r)
所以
\r -> (\r' -> runReader sth (const (f r) r')) r
变成
\r -> runReader sth (f r)
与local f sth
完全相同。
现在的问题是,您的编译器是否足够聪明,可以自行执行此 t运行sformation?如果非要我猜的话,我猜 GHC 是 确实足够聪明,因为它都是基本的代数等式。但幸运的是,我没有去猜测,因为我可以检查。
使用这两个来源,test1.hs
:
import Control.Monad.Reader
main :: IO ()
main = print $ runReader m "hello"
f = (++"!!!")
m = ask >>= \r -> local (const (f r)) ask
和test2.hs
:
import Control.Monad.Reader
main :: IO ()
main = print $ runReader m "hello"
f = (++"!!!")
m = local f ask
I 运行 ghc -O2 -ddump-simpl
在他们两个上(使用 GHC 7.10.3)。猜猜是什么,核心文件仅在 运行domized 名称上有所不同。
G运行ted,没有 -O2
输出差异很大。但这是意料之中的。
我考虑下面两个表达式是否等价:
env <- ask
local (\_ -> env) sth
和:
local (\env -> env) sth
如果不是,参数 lambda 被带到哪里?
不太确定你的问题是什么,但让我们试一试。
所以你问的是
ask >>= \env -> local (const env) sth
和
local id sth
是等价的。
就效果而言 -- 当然是。更重要的是,它们都等同于 sth
。让我们考虑一个稍微复杂一点的例子:
ask >>= \env -> local (const (f env)) sth
对
local f sth
现在让我们试着了解它是如何工作的。
local
定义为
local :: (r -> r) -> Reader r a -> Reader r a
local f m = Reader $ runReader m . f
(我简化了一点,因为它实际上是通过 withReaderT
为 ReaderT
定义的,但它理解了这个想法)
提醒一下,我们可以假设
runReader :: Reader r a -> r -> a
此外,提醒一下 Reader r a
是一种包装 r -> a
.
现在,>>=
在这里做什么?简单,
m >>= k = Reader $ \r -> runReader (k (runReader m r)) r
因此,它需要 m
,从中提取值,并将其传递给 k
,同时将实际环境 r
作为自由参数。作为一个行为良好的 monadic bind 应该,所以这里不足为奇。
还要注意,ask
只是 Reader id
现在,让我们完全放弃整个 Reader
东西,将我们的表达式重写为函数。
ask >>= \env -> local (const (f env)) sth
然后变成
\r -> (\r' -> runReader sth (const (f r) r')) r
和
local f sth
变成
\r -> runReader sth (f r)
现在稍微眯一下,就可以看出这两者在效果上是等价的。的确,
const (f r) r' = f r
(\r' -> runReader sth (f r)) r = (\_ -> runReader sth (f r)) r
= runReader sth (f r)
所以
\r -> (\r' -> runReader sth (const (f r) r')) r
变成
\r -> runReader sth (f r)
与local f sth
完全相同。
现在的问题是,您的编译器是否足够聪明,可以自行执行此 t运行sformation?如果非要我猜的话,我猜 GHC 是 确实足够聪明,因为它都是基本的代数等式。但幸运的是,我没有去猜测,因为我可以检查。
使用这两个来源,test1.hs
:
import Control.Monad.Reader
main :: IO ()
main = print $ runReader m "hello"
f = (++"!!!")
m = ask >>= \r -> local (const (f r)) ask
和test2.hs
:
import Control.Monad.Reader
main :: IO ()
main = print $ runReader m "hello"
f = (++"!!!")
m = local f ask
I 运行 ghc -O2 -ddump-simpl
在他们两个上(使用 GHC 7.10.3)。猜猜是什么,核心文件仅在 运行domized 名称上有所不同。
G运行ted,没有 -O2
输出差异很大。但这是意料之中的。