功能本地haskell阅读器monad

时间:2016-05-16 13:37:14

标签: haskell monads

我考虑以下两个表达式是否相同:

env <- ask
local (\_ -> env) sth

local (\env -> env) sth

如果没有,它被用于参数lambda?

1 个答案:

答案 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

(我正在简化一点,因为它实际上是通过ReaderTwithReaderT定义的,但它可以得到这个想法。

提醒一下,我们可以假设

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输出相差很大。但这是预期的。