I am trying to do a global counter using monads in Haskell, I want to get the incremented value every time I use the monad counter, but I am kind of stuck getting the same value each time!
The code list is as follows:
module CounterMonad where
data Counter a = C (Int -> (Int, a))
--reset the counter
new :: Counter ()
new = C $ \_ -> (0, ())
-- increment the counter:
--inc :: Counter Int
--inc = C $ \n -> (n+1, n)
inc = get >>= \s -> (put (s+1))
-- returning the current value of the counter
get :: Counter Int
get = C $ \n -> (n, n)
--
put x = C $ \n -> (x, x)
--return is nop, >>= is sequential exectuion
instance Monad Counter where
return r = C $ \n -> (n, r)
(>>=) (C f) g = C $ \n0 -> let (n1, r1) = f n0
C g' = g r1
in g' n1
run :: Counter a -> a
run (C f) = snd (f 0)
tickC = do
inc
c <- get
return c
When I try to execute as run tickC, it always returns 1.
What I want is everytime I run tickC, it returns the incremented value, like 1, ,2, 3,4 ….
I know there must be some stupid problem lying there, can you guys point out how?
This is correct behavior. Every time you call
run tickC, therunfunction evaluates the operation with the counter set to zero. Essentially, each time you callrunyou get a different “copy” of the counter initialized to zero.If you want to have the counter increment each time, you have to execute all operations in the same call to
run. For example,This is true of all monads, including
IO(ignoring the “unsafe” operations). Since the only way to run anIOoperation is throughmain, theIOmonad is threaded through every function that uses it.So if you want a global counter, that counter has to be managed by a monad that is used globally (i.e., by every function that needs to access it). You can either use your
Countermonad globally or put the counter in theIOmonad. It seems to be accepted design practice to put state like this in your own monad, but of course, it depends on the application andIOis fine too.You may also wish to look at
Control.Monad.State, which would allow you to define your monad with much less typing. Something like: