首页 > 解决方案 > 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

似乎我们可以使用asks或来计算相同的结果local

是否有任何结果无法从其中任何一个计算得出?

现在,我只能说 usinglocal允许我将changeEnv改变环境的函数 ( getLength) 和返回 Reader 结果的函数 () 分开。但是,同样可以实现使用asks

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

标签: haskellmonadsreader

解决方案


我们有asks f = local f ask任何 f,所以你总是可以askslocal. 虽然通常你会认为asks ffmap 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似乎更好。


推荐阅读