帮助读者monad

时间:2010-08-10 17:11:37

标签: haskell monads

我是haskell的新手,我必须编写一个程序上下文感知,所以我认为我可以使用Reader Monad来保持从文件中读取上下文,我知道如何读取将内容放在列表中的文件像[([Char],[Char])]这样的tuplessomething,但我不知道如何实现Reader Monad,使环境可用于我的程序的所有组件而不使用命令式样式,特别是我不知道如何设置和使用环境,据我所知,我应该将它作为参数提供给需要环境的所有函数使用runReader函数env,但我很困惑,有人可以给我一些指示或一个很好的教程吗?提前谢谢

3 个答案:

答案 0 :(得分:8)

使用任何“普通”monad [0] 的基本方案完全相同。基本上:

  • 如果您愿意,使用do表示法编写返回monadic类型值的函数,就像您编写IO函数main一样。
  • 使用您正在使用的monad的任何特定功能。
  • 使用标准规则相互调用这些函数:
    • 使用<-绑定相同 monad中的值以获取值“inside”,从而导致其他值“运行”。
    • 使用let绑定任何其他值,使其独立于monadic结构。
  • 使用特定monad的专用“run”函数来评估monadic计算并获得最终结果。

这样做,monad描述的额外功能的所有杂乱细节(在这种情况下,传递额外的环境参数)都会自动处理。

现在,通常的Reader操作是asklocal

  • ask是保持环境的一元价值;在do区块中,您使用它的方式与getLine monad中使用IO的方式相同。
  • local接受一个在Reader monad中提供新环境和计算的函数,在前者修改的环境中运行后者,然后获取结果并将其放入当前函数。换句话说,它使用本地修改的environemnt运行子计算。

“run”函数是创造性命名的runReader,它只是在Reader monad和环境值中进行计算,使用后者运行前者,并在monad之外返回最终结果。

作为一个例子,这里有一些函数在Reader monad中进行一些无意义的计算,其中环境是一个“最大值”,表示何时停止:

import Control.Monad.Reader

computeUpToMax :: (Int -> Int) -> Int -> Reader Int [Maybe Int]
computeUpToMax f x = do 
    maxVal <- ask
    let y = f x
    if y > maxVal
        then return []
        else do zs <- local (subtract y) (computeUpToMax f y)
                z <- frob y
                return (z:zs)

frob :: Int -> Reader Int (Maybe Int)
frob y = do
    maxVal <- ask
    let z = maxVal - y
    if z == y 
        then return Nothing
        else return $ Just z

要运行它,你可以使用这样的东西:

> runReader (computeUpToMax (+ 1) 0) 9
[Just 8, Just 6, Nothing]

...其中9是初始环境。

几乎完全相同的结构可以与其他monad一起使用,例如StateMaybe[],但在后两种情况下,您通常只使用最终结果monadic结果值而不是使用“run”函数。

[0] :正常意味着不涉及编译器魔法,最明显的“异常”monad当然是IO

答案 1 :(得分:5)

我认为如果你看看如何在不使用Reader的情况下解决这个问题,然后比较翻译版本,这是最简单的。这是我正在处理的程序的精简示例,其中环境是一组用于更新显示的回调函数。它稍微复杂一点,因为它使用ReaderT而不是Reader,但一切都以基本相同的方式工作。

runProcess :: Env -> State -> Action -> IO State
runProcess env state action = do
  newstate <- processAction state action
  let ufunc = mainUFunc env              -- get the callback to update the display
  ufunc newstate                         -- update the display
  return newstate

现在我将更改它以使用Reader monad传递环境。由于代码已经在IO中,因此必须使用monad转换器版本ReaderT

runProcessR :: State -> Action -> ReaderT Env IO State
runProcessR state action = do
  newstate <- lift $ processAction state action
  env <- ask                              -- get the environment from the reader
  liftIO $ (mainUFunc env) newstate       -- updating is in IO; it needs to be lifted
  return newstate

此时,程序的主循环基本上是:

loop :: State -> ReaderT Env IO ()
loop = do
  action <- liftIO getAction
  if action == EndLoop
    then return ()
    else do
      st' <- processActionR st action
      loop st'

mainLoop :: IO ()
mainLoop = do
  env <- setUpCallbacks
  let st = initState
  runReaderT $ loop st

这就是你如何使用Reader。以前用于获取环境参数的每个函数都不再需要。不采取环境的功能可以直接使用,如果它们是monadic,则可以解除。

答案 2 :(得分:1)

这是恕我直言最佳monad资源 - All About Monads,这里是Reader monad的一部分。