使用reader monad直接传递值有什么好处?

wiz*_*zup 8 monads haskell

我试图了解何时使用阅读器monad,但我还没有找到一个很好的用法示例.我很确定我对这个主题知之甚少.

考虑这个示例代码:

import Control.Monad.Reader

data Env = Env
         { eInt :: Int
         , eStr :: String
         }

calculateR :: Reader Env Int
calculateR = do
    e <- ask
    return $ eInt e

calculate :: Env -> Int
calculate = eInt

main :: IO ()
main = do
    let env = Env { eInt = 1, eStr = "hello"}
    let a = runReader calculateR env
    let b = calculate env
    print (a,b)
Run Code Online (Sandbox Code Playgroud)

如果我想传遍全局Env以便能够从任何函数访问它,我应该为此目的使用阅读器monad吗?

Env直接传递给函数相比,有什么好处吗?

如果我理解正确的话,无论是calculatecalculateR是纯(在这个意义上,他们将不能够进行任何更改env),并且都Env在他们的类型签名,告诉他们可能读值的形式env在他们的计算中使用.

jak*_*iel 14

当你想要组成两个函数时,优势就来了.

f :: a -> Reader Env b
g :: b -> Reader Env c

g <=< f
Run Code Online (Sandbox Code Playgroud)

或者用do符号表示

\a -> do
    b <- f a
    g b
Run Code Online (Sandbox Code Playgroud)

VS

f :: a -> Env -> b
g :: b -> Env -> c

\a e -> g (f a e) e
Run Code Online (Sandbox Code Playgroud)

在后者中你必须不断地自己传递环境,而monad实例为你提供了一个组合,为你做.随着涉及更多功能,手动组合将很快变得不必要地冗长和复杂.

读者然后也为您提供local.


Ben*_*Ben 5

根据我的经验,它最有用的时候:

  1. 你有很多函数需要深度调用图,而调用图上的一些东西需要环境,但很多中间函数却没有.然后读者monad为你处理所有的管道.特别是当你使用monad变换器堆栈时(特别是你的堆栈的类型或新类型别名)所以你已经用monadic风格写了; 在这种情况下,您可能很晚才意识到您需要环境,更改monad堆栈的定义,更改入口点以通过环境,更改使用站点以引用它,而不必触摸任何中间的代码函数(如果你有一个堆栈的名字,甚至可能不是它们的类型).

  2. 您正在提供一个接口,其中许多客户端代码将函数传递给您,并且您希望它们能够访问环境,但期望它们中的许多实际上不需要它.再次,当你已经拥有一个monad变换器堆栈时,你可以将读者插入其中,这样做的好处就更大了; 现有的客户端代码已经采用monadic风格,可以通过忽略环境来改变,而可以编写使用它的新函数.

但是对于任何小到足以适应堆栈溢出的帖子,普通函数几乎总是更合适.