2

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?

1 Answers1

6

This is correct behavior. Every time you call run tickC, the run function evaluates the operation with the counter set to zero. Essentially, each time you call run you 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,

tickMany = do
    x <- tickC
    y <- tickC
    z <- tickC
    return [x, y, z]

> run tickMany
[1, 2, 3]

This is true of all monads, including IO (ignoring the "unsafe" operations). Since the only way to run an IO operation is through main, the IO monad 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 Counter monad globally or put the counter in the IO monad. It seems to be accepted design practice to put state like this in your own monad, but of course, it depends on the application and IO is 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:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.State
newtype Counter a = Counter (State Int a) deriving (Monad)
...
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • Thanks Dietrich~~ But I am not quite understanding how to do: 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 Counter monad globally... –  Jun 12 '11 at 23:45
  • So if you want any function to use the counter, it has to be in the `Counter` monad. Then, any function which uses that function also has to be in the `Counter` monad, and so on. – Dietrich Epp Jun 13 '11 at 00:25
  • Put Monadic transformers onto your list of things to learn to undrstand how to manage multiple monads at once. – fuz Jun 13 '11 at 10:14
  • @freezdom http://stackoverflow.com/questions/6302249/how-to-implement-a-global-counter-using-monad/6349651#6349651 – Deech Jun 14 '11 at 20:31