微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Reader monad中本地和询问功能的目的是什么

如何解决Reader monad中本地和询问功能的目的是什么

当我尝试熟悉Haskell中Reader Monad的用法时,我注意到mtl库中提供了两个类似的功能

asks :: MonadReader r m => (r -> a) -> m a

local :: MonadReader r m => (r -> r) -> m a -> m a

例如,

import Control.Monad.Reader

changeEnv :: String -> String
changeEnv = ("Prefix " ++)

getLength :: Reader String Int
getLength = do
  e <- ask
  return $ length e

runReader (local changeEnv getLength) "123" 
10

runReader (asks (length . changeEnv)) "123"
10

runReader (asks (length . local changeEnv id)) "123"
10

似乎我们可以使用askslocal来计算相同的结果。

是否存在无法从其中任何一个进行计算的结果?

就目前而言,我只能说使用local允许我分离更改环境的函数changeEnv)和返回阅读器结果的函数getLength)。但是,使用asks也可以达到同样的效果

有什么特殊原因需要我们两者吗?

解决方法

对于任何f,我们都有asks f = local f ask,因此您可以 始终用asks来写local。虽然通常你会 将asks f视为fmap f ask的简写。

从另一个方向来看,考虑类似

local changeEnv (do x <- getLength
                    y <- getReverse
                    pure (replicate x y))

您通常如何使用asks来做到这一点?您必须包括 整个身体,并明确地将环境放入其中:

asks ((\s -> replicate (length s) (reverse s)) . changeEnv)

如果愿意,您可以随时这样做,因为您可以随时翻译 任何Reader计算都转换为普通函数。但是你可能会 完全不要使用Reader。

这也不意味着local也是必要的-只是 嵌套阅读器的便利,您随时可以使用runReader 直接代替:

do s' <- asks changeEnv
   let x = runReader (do ...) s'
   ...

我想如果您愿意的话,可以将其全部塞入asks

asks (runReader (do ...) . changeEnv)

但是使用local似乎更好。

,

对于您而言,您可以用涉及cat ~/.ssh/id_rsa.pub的等效表达式替换local changeEnv getLength。但是,为此,您必须检查asks的代码,并在每次使用getLength的代码时手动插入changeEnv

ask的重点是允许进行这种转换而不必重写local的所有代码。想象一下一个极端的情况,其中有一个非常复杂的getLength而不是getLength,可能还调用了其他操作,因此我们总共要触摸几千行代码。

然后我们找到一个要替换的action。在这种情况下,我们只需要在所有正确的位置插入local f action,就需要重写action及其依赖关系的所有代码。这很长,而且效率也很低,因为通过这种方式我们多次重新计算f

f避免了这种情况。当然,严格来说,我们不必使用local,因为我们可以通过local构造函数上的模式匹配来破坏抽象(或滥用Reader),以便我们可以预先在正确的位置撰写runReader。但是,这样做的话,我们将重新实现f

此外,请注意,local不仅适用于简单的local单子,而且适用于类Reader a的所有单子,其中MonadReader可能很复杂一个。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。