帮助读者monad

arp*_*pho 13 monads haskell

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

C. *_*ann 8

使用任何"普通"monad [0]的基本方案几乎全面相同.实质上:

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

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

现在,一般的阅读器操作asklocal:

  • ask是保持环境的一元价值; 在一个do块中你使用它就像你getLineIOmonad中使用一样.
  • 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
Run Code Online (Sandbox Code Playgroud)

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

> runReader (computeUpToMax (+ 1) 0) 9
[Just 8, Just 6, Nothing]
Run Code Online (Sandbox Code Playgroud)

...... 9初始环境在哪里

几乎一模一样的结构可以与其他单子,比如可以使用State,Maybe或者[],虽然在后两种情况下,你通常只需要使用一元的最终结果值,而不是使用"运行"功能.

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


Joh*_*n L 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
Run Code Online (Sandbox Code Playgroud)

现在我将其更改为使用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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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