OrenIshShalom
OrenIshShalom

Reputation: 7162

Manipulating Haskell Monad State

Somewhat similar to this question, I'm trying to figure out how to move around a Haskell Monad state. Each Employee in a team is replaced with a corresponding Employee' while maintaining some simple state. Here is the code:

module Main( main ) where
import Control.Monad.State

data Employee  = EmployeeSW  Int Int | EmployeeHW Int String deriving ( Show )
data Employee' = EmployeeSW'     Int | EmployeeHW'    String deriving ( Show )

scanTeam :: [Employee] -> State (Int,Int) [Employee']
scanTeam [    ] = return []
scanTeam (p:ps) = scanEmployee p -- : scanTeam ps ???

scanEmployee :: Employee -> State (Int,Int) Employee'
scanEmployee (EmployeeSW id s) = do
    (num,raise) <- get
    put (num+1,raise)
    return (EmployeeSW' (s+raise))
scanEmployee (EmployeeHW id s) = do
    (num,raise) <- get
    put (num+1,raise)
    return (EmployeeHW' (s++(show raise)))

startState = (0,3000)

t = [(EmployeeHW 77 "Hundred"),(EmployeeSW 66 500),(EmployeeSW 32 200)]

main = print $ evalState (scanTeam t) startState

I want to eventually concatenate scanEmployee p with scanTeam ps, so I tried to extract the pieces of scanEmployee p and somehow glue them together with scanTeam ps. So far I failed miserably. Actually, I'm not even sure the state can be moved around between them (?).

Upvotes: 1

Views: 76

Answers (1)

Benjamin Hodgson
Benjamin Hodgson

Reputation: 44634

Since State is a monad, you can use do notation to define State computations. (State's instance of Monad plumbs the state through, so the ending state of one statement in the do block becomes the starting state of the next.)

So, in a do block, I'm going to:

  1. Process the first Employee in the list to get a new Employee
  2. Process the rest of the list recursively
  3. Put the two results back together and use them as the return value for the Stateful computation.
scanTeam :: [Employee] -> State (Int,Int) [Employee']
scanTeam [    ] = return []
scanTeam (p:ps) = do
    newP <- scanEmployee p
    newPs <- scanTeam ps
    return (newP:newPs)

It turns out that "map in a monadic context" is pretty useful in general, so it's present in the standard prelude as mapM :: Monad m => (a -> m b) -> [a] -> m [b] (aka traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b), if you're ready to go down the rabbit hole).

scanTeam = mapM scanEmployee

Upvotes: 4

Related Questions