Reputation: 3247
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
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
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
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
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
:
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.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