Haskell 本地及;读卡器monad中的函数

Haskell 本地及;读卡器monad中的函数,haskell,monads,reader,Haskell,Monads,Reader,当我试图熟悉Haskell中Reader monad的用法时,我注意到在mtl库中提供了两个类似的函数 asks::monawarder r m=>(r->a)->ma local::monawarder r m=>(r->r)->ma->ma 比如说, import Control.Monad.Reader changeev::String->String changeev=(“前缀”+) getLength::读取器字符串Int getLength=do e对于任何f,我们都有asks f=

当我试图熟悉Haskell中Reader monad的用法时,我注意到在
mtl
库中提供了两个类似的函数

asks::monawarder r m=>(r->a)->ma

local::monawarder r m=>(r->r)->ma->ma

比如说,

import Control.Monad.Reader
changeev::String->String
changeev=(“前缀”+)
getLength::读取器字符串Int
getLength=do

e对于任何f,我们都有
asks f=local f ask
,所以确实可以 始终按照
local
编写
ask
。虽然通常你会 把
ask f
看作是
fmap f ask
的简写

但在另一方向,考虑一些类似

local changeEnv (do x <- getLength
                    y <- getReverse
                    pure (replicate x y))
如果你愿意,你可以随时这样做,因为你可以随时翻译 将任何读取器计算转换为普通函数。但是你也可以 我们根本不用读卡器

这并不意味着
local
是必要的,它只是一个 方便嵌套读卡器,并且您可以始终使用
runReader
直接地,而不是像:

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

但是使用
local
似乎更好。

在您的例子中,您可以用一个包含
asks
的等价表达式替换
local changeev getLength
。但是,要执行此操作,您必须检查
getLength
的代码,并在每次代码使用
ask
时手动插入
changeev

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

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

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


此外,请注意,
local
不仅适用于简单的
Reader a
monad,而且适用于
monader
类的所有monad,其中
操作可能非常复杂。

然而,
local
可能不应该在该类中@dfeuer是否有一种方法可以从其他类方法中以通用的方式定义它?没有,这就是为什么最好给它自己的类。对于所有候选实例,实现起来并不一定容易(或可能)。
do s' <- asks changeEnv
   let x = runReader (do ...) s'
   ...
asks (runReader (do ...) . changeEnv)