haskellfunctional-programmingmonadsfoldstate-monad

Extracting, manipulating and accumulating values within a list of monads in Haskell


I am trying to create a function that 'extracts' and evaluate the value of a each monad in a list of monads such that a final value - accumulated or so - can be returned. The signature for such a function would be something like accCurryingMonad :: [SomeCurryingMonad a d] -> SomeCurryingMonad a d. To make this harder the value of each monad is a tupple. Furthermore if one of the values evaluated 'fails' or returns 'nothing' the function is still to proceed with the hitherto accumulated value, and use it for the rest of the chain of monadic operations in the list.

For the purpose of solving my problem I have had a re-read of the State s a monad as described in LYAH Stacking Manip, and have tried to rewrite the code of the 'Stack and stones' problem to fit my purpose, since I suspect that this is a problem that can be solved with foldM.

Heres the rewritten MRE code:

import Control.Monad.State

type Stack = [Int]

pop :: State Stack Int
pop = do
    stack <- get
    case stack of
        (x:xs) -> do
            put xs
            return x
        _ -> error "Empty stack"

push :: Int -> State Stack ()
push a = do
    stack <- get
    put (a:stack)

-- Perform a single step of the computation using State monad
singleStep :: Int -> State Stack Int
singleStep value = do
    push value    
    pop

-- Perform the computation 10 times using foldM with State monad
stackManip10Times :: State Stack Int
stackManip10Times = foldM (\_x v -> v) 0 [singleStep 10, singleStep 20, singleStep 30]

main :: IO ()
main = do
    let initialStack = [200, 300, 400, 500] :: Stack
        (result, finalStack) = runState stackManip10Times initialStack
    putStrLn $ "Result: " ++ show result
    putStrLn $ "Final Stack: " ++ show finalStack

How can I use the foldM (if that is the right approach, that is) to turn:

stackManip10Times = foldM (\_x v -> v) 0 [singleStep 10, singleStep 20, singleStep 30]

into something like this function which doesn't take a generic function (e. g singleStep v), and yet evaluates and manipulate the value within the monad, and returns the result of all of the manipulations no matter what monadic operation, within a single monad:

stackManip10Times :: State Stack Int
stackManip10Times = foldM (\_x v -> v) () [push 10, pop, push 20]

Solution

  • This is how I interpret the question, I may well be grossly mistaken.

    Let's look at the implementation of sequence (for lists, not for generic Traversables).

    sequence = mapM id
    
    mapM :: Monad m => (a -> m b) -> [a] -> m [b]
    mapM f as = foldr k (return []) as
                where
                  k a r = do { x <- f a; xs <- r; return (x:xs) }
    

    This has no provision for catching and discarding errors, so let's add it.

    sequenceNF :: MonadError e m => [m a] -> m [a]
    sequenceNF = mapMNF id
    
    mapMNF :: MonadError e m => (a -> m b) -> [a] -> m [b]
    mapMNF f as = foldr k (return []) as
                where k a r = do { 
                   x <- f a; xs <- r; return (x:xs) 
                } `catchError` (\_ -> r)
    

    Now we can:

    sequenceNF [Just 42, Nothing, Just 13] 
    -- => Just [42, 13]
    
    sequenceNF [print 42, ioError (userError "Oops"), print "Hi"] 
    -- => IO [(), ()]
    -- will print 42 and "Hi" when executed 
    
    sequenceNF [
       print "abc",
       do { print 42; print "def" },
       do { print "Hi"; ioError (userError "Oops"); print "Bye"; },
       print "Ok" ]
    -- => IO [(), (), ()]
    -- will print: "abc" 42 "def" "Hi" "Ok"
    -- note there are 4 elements in the input list
    -- but only 3 elements in the output list
    -- execution of the third element failed, so no value collected
    -- although side effects of it are still executed