Haskell 本地及;读卡器monad中的函数
当我试图熟悉Haskell中Reader 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=
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)