Rog Matthews
Rog Matthews

Reputation: 3247

Implementation of State Monad

I am working on a simple example to implement the state monad in Haskell. I have made a data type GlobState using record syntax. Functions incr1, incr2 and incr3 are there to increment the respective record elements. The code i have written is:

module StateExample where
import Control.Monad.State

data GlobState = GlobState { c1 :: Int, c2:: Int, c3:: Int} deriving (Show)

newGlobState:: GlobState
newGlobState = GlobState { c1=0,c2=0,c3=0 }

--incr1 :: State GlobState ()
incr1 = do
    gets c1
    modify (+1)

--incr2 :: State GlobState ()
incr2 = do
    gets c2
    modify(+1)

incr3 = do
    gets c3
    modify(+1)

main =  do
    let ((),a1) = flip runState newGlobState $ do
        x<- incr1
        y<- incr2 x
        z<- incr1 y
        return z
    print a1

This program is not giving the desired output. And i am not sure about the signature of increment functions or if gets is doing what i intend to do. Please help.

How can i make changes to the code so that instead of defining the different increment functions, i need to make only one increment function. i intend to do like:

incr :: String-> State GlobState ()
incr x = do
modify(\g -> g {x =x g + 1})

Upvotes: 2

Views: 973

Answers (4)

Cactus
Cactus

Reputation: 27646

You could use modify from the fclabels package and write it like

incr1 = modify c1 (+1)
incr2 = modify c2 (+1)
incr3 = modify c3 (+1)

I don't think it gets any more intentional than that.

Upvotes: 3

user1078763
user1078763

Reputation: 728

Here is code that works, maybe you can build up from that.

module StateExample where
import Control.Monad.State

data GlobState = GlobState { c1 :: Int, c2:: Int, c3:: Int} deriving (Show)

newGlobState:: GlobState
newGlobState = GlobState { c1=0,c2=0,c3=0 }

incr1 :: State GlobState ()
incr1 = modify (\g -> g {c1 = c1 g +1})

incr2 :: State GlobState ()
incr2 = do
  modify (\g -> g {c2 = c2 g +1})


incr3 :: State GlobState ()
incr3 = do
  modify (\g -> g {c3 = c3 g +1})


main :: IO ()
main =  do
  let a1 = flip execState newGlobState $ do
      incr1
      incr2
      incr1
  print a1

Upvotes: 2

Oliver
Oliver

Reputation: 2401

The types are a problem here - in this context:

modify :: (GlobState  -> GlobState) -> State GlobState  ()

This is a problem because:

modify (+1) :: (Int -> Int) -> State Int ()

So you need to write your function in terms of a GlobState like this:

incr1 = modify (\GlobState c1 c2 c3 -> GlobState (c1 + 1) c2 c3)

Alternative you can get and set the state separately:

incr1 = do
  (GlobState c1 c2 c3) <- get
  put (GlobState (c1 + 1) c2 c3)

Upvotes: 3

dave4420
dave4420

Reputation: 47072

Your types are right, at least.

incr1 :: State GlobState ()
incr1 = modify (\state -> state {c1 = 1 + c1 state})

Your modify (+1) would be fine if you were working in State Int (), say, but we're not. You seem to think that gets focusses the following lines onto a particular field of the state --- but it doesn't.

incr2 and incr3 need to be changed similarly.

As to main:

  1. You need to indent your code properly. You appear to want the print a1 to be part of the outer do block, but not part of the inner do block. Without indentation, the compiler cannot figure that out.
  2. You are (attempting to) manually thread the state, but the whole point about State is that its monadic/applicative plumbing does that for you.
main = do let ((), a1) = flip runState newGlobState $ do incr1
                                                         incr2
                                                         incr1
          print a1

Upvotes: 6

Related Questions