help with reader monad

后端 未结 3 513
暗喜
暗喜 2021-01-04 04:29

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 p

3条回答
  •  -上瘾入骨i
    2021-01-04 05:05

    The basic scheme for using any "normal" monad[0] is pretty much the same across the board. 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.
    • Make use of any specific functions for the monad you're working with.
    • 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".
      • Bind any other value using let, leaving it independent of the monadic structure.
    • Use a particular monad's specialized "run" function to evaluate the monadic computation and get the final result.

    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.

    Now, the usual Reader operations are ask and local:

    • ask is a monadic value holding the environment; in a do block you use it the same way you'd use something like getLine in the IO monad.
    • 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. In other words, it runs a sub-computation with a locally modified 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.

    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:

    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.

    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.

    [0]: Where normal means not involving compiler magic, the most obvious "abnormal" monad of course being IO.

提交回复
热议问题