简体   繁体   English

帮助读者monad

[英]help with reader monad

I am new at haskell, I have to write a program context-aware,so I thought I can use the Reader Monad for keeping the context read from a file, I know how to read the file puting the content in a list of tuplessomething like [([Char],[Char])], but I do not know how to implement the Reader Monad for making the environment available to all the components of my program without using imperative style, In particular I do not know how to set and use the environment, as far as I understood I should give it as parameter to all the functions that need the environment with runReader function env, but I am very confused, can somebody give me some indications or a good tutorial? 我是haskell的新手,我必须编写一个程序上下文感知,所以我认为我可以使用Reader Monad来保持从文件中读取上下文,我知道如何读取文件将内容放在像tuplessomething这样的列表中[([Char],[Char])],但我不知道如何实现Reader Monad使环境可用于我的程序的所有组件而不使用命令式样式,特别是我不知道如何设置和使用环境,据我所知,我应该将它作为参数提供给需要环境的所有函数使用runReader函数env,但我很困惑,有人可以给我一些指示或一个很好的教程吗? thanks in advance 提前致谢

The basic scheme for using any "normal" monad [0] is pretty much the same across the board. 使用任何“普通”monad [0]的基本方案几乎全面相同。 Essentially: 实质上:

  • Write functions that return a value of monadic type, using do notation if you like, just like you'd write an IO function like main . 编写返回monadic类型值的函数,如果你愿意,可以使用do notation,就像你编写像main这样的IO函数。
  • Make use of any specific functions for the monad you're working with. 利用您正在使用的monad的任何特定功能。
  • Call these functions from each other, using the standard rule: 使用标准规则相互调用这些函数:
    • Bind a value from the same monad using a <- to get at the value "inside", causing the other value to be "run". 使用<-绑定来自同一 monad的值以获取值“inside”,从而导致另一个值“run”。
    • Bind any other value using let , leaving it independent of the monadic structure. 使用let绑定任何其他值,使其独立于monadic结构。
  • Use a particular monad's specialized "run" function to evaluate the monadic computation and get the final result. 使用特定monad的专用“run”函数来评估monadic计算并获得最终结果。

Do that, and all the messy details of the extra functionality described by the monad (in this case, passing an extra environment parameter around) are handled automatically. 这样做,并且monad描述的额外功能的所有杂乱细节(在这种情况下,传递额外的环境参数)都是自动处理的。

Now, the usual Reader operations are ask and local : 现在,通常的Reader操作是asklocal

  • ask is a monadic value holding the environment; ask是保持环境的一元价值; in a do block you use it the same way you'd use something like getLine in the IO monad. do块中你使用它的方式与在IO monad中使用getLine之类的方法相同。
  • local takes a function that provides a new environment and a computation in the Reader monad, runs the latter in an environment modified by the former, then takes the result and puts it into the current function. local在Reader monad中提供一个提供新环境和计算的函数,在前者修改的环境中运行后者,然后获取结果并将其放入当前函数中。 In other words, it runs a sub-computation with a locally modified environemnt. 换句话说,它使用本地修改的environemnt运行子计算。

The "run" function is the creatively-named runReader , which simply takes a computation in the Reader monad and an environment value, runs the former using the latter, and returns the final result outside of the monad. “run”函数是创造性命名的runReader ,它简单地在Reader monad和环境值中进行计算,使用后者运行前者,并将最终结果返回到monad之外。

As an example, here's some functions doing some meaningless calculation in a Reader monad, where the environment is a "maximum value" that says when to stop: 举个例子,这里有一些函数在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

To run it, you'd use something like this: 要运行它,你可以使用这样的东西:

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

...where 9 is the initial environment. ......其中9是初始环境。

Almost exactly the same structure can be used with other monads, such as State , Maybe , or [] , though in the latter two cases you'd typically just use the final monadic result value instead of using a "run" function. 几乎完全相同的结构可以与其他monad一起使用,例如StateMaybe[] ,但在后两种情况下,您通常只使用最终的monadic结果值而不是使用“run”函数。

[0] : Where normal means not involving compiler magic, the most obvious "abnormal" monad of course being IO . [0] :正常意味着不涉及编译器魔法,最明显的“异常”monad当然是IO

I think it's easiest if you look at how you would solve this problem without using Reader, then compare the translated version. 我认为如果你看看如何在不使用Reader的情况下解决这个问题,然后比较翻译版本,这是最简单的。 Here's a trimmed-down example from a program I'm working on where the environment is a set of callback functions to update the display. 这是我正在处理的程序的精简示例,其中环境是一组用于更新显示的回调函数。 It's slightly more complicated because it uses ReaderT instead of Reader, but everything works in basically the same way. 它稍微复杂一点,因为它使用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

Now I'll change it to use the Reader monad to pass along the environment. 现在我将其更改为使用Reader monad传递环境。 Since the code was already in IO, it's necessary to use the monad transformer version, ReaderT . 由于代码已经在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

At this point, the program's main loop will essentially be: 此时,程序的主循环基本上是:

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

So that's how you can use Reader. 这就是你如何使用Reader。 Every function that used to take an environment parameter no longer needs to. 以前用于获取环境参数的每个函数都不再需要。 Functions that don't take the environment can be used directly or lifted if they're monadic. 不采取环境的功能可以直接使用,如果它们是monadic可以解除。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM