【问题标题】:What is the purpose of local & asks functions in Reader monadReader monad 中本地和询问函数的目的是什么
【发布时间】:2021-02-16 05:48:05
【问题描述】:

当我尝试熟悉 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 也可以实现同样的效果。

有什么特殊的原因我们需要它们吗?

【问题讨论】:

    标签: haskell monads reader


    【解决方案1】:

    对于任何 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 似乎更好。

    【讨论】:

      【解决方案2】:

      在您的情况下,您可以将local changeEnv getLength 替换为涉及asks 的等效表达式。但是,为此,您必须检查getLength 的代码,并在每次使用ask 的代码时手动插入changeEnv

      local 的重点是允许这种转换,而不必重写getLength 的所有代码。想象一个极端的场景,我们有一个非常复杂的action,而不是getLength,可能还会调用其他操作,所以我们总共要接触几千行代码。

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

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

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

      【讨论】:

      • 但是,local 可能不应该在那个班级!
      • @dfeuer 有没有办法从其他类方法中以通用方式定义它?
      • 没有,这就是为什么最好给它自己的类。它不一定容易(或可能?)为所有候选实例实现。
      猜你喜欢
      • 2015-12-31
      • 2017-08-16
      • 1970-01-01
      • 2016-08-03
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 2021-01-18
      • 1970-01-01
      相关资源
      最近更新 更多