我考虑以下两个表达式是否相同:
env <- ask
local (\_ -> env) sth
和
local (\env -> env) sth
如果没有,它被用于参数lambda?
答案 0 :(得分:3)
不确定你的问题是什么,但试试吧。
所以你问是否
ask >>= \env -> local (const env) sth
和
local id sth
是等价的。
就效果而言 - 确定它们是。更重要的是,它们只相当于sth
。让我们考虑一个更复杂的例子:
ask >>= \env -> local (const (f env)) sth
VS
local f sth
现在让我们试着了解它是如何运作的。
local
定义为
local :: (r -> r) -> Reader r a -> Reader r a
local f m = Reader $ runReader m . f
(我正在简化一点,因为它实际上是通过ReaderT
为withReaderT
定义的,但它可以得到这个想法。
提醒一下,我们可以假设
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
完全相同。
现在的问题是,您的编译器是否足够聪明,可以自行完成此转换?如果我不得不猜测,我猜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
我在两者上运行ghc -O2 -ddump-simpl
(使用GHC 7.10.3)。猜猜看,核心文件只有随机名称才有区别。
当然,没有-O2
输出相差很大。但这是预期的。