shotor
shotor

Reputation: 1037

Simple counter in IO

I'm trying to create a simple counter which increases by 1 indefinitely, using IO.

I've been scratching my head ever since...

Ideally, I would like to do something along the lines of

tick = do putStr (counter)
          counter + 1
    where counter = 0

Then repeat the process. Then repeat the first 2 expressions. Or something along the lines of:

tick = tick'
       where 
           counter = 1
           tick' counter | counter > 0 = do putStrLn (show counter)
                                            tick' (counter + 1)
                         | otherwise = tick

Which gives me errors :/

Any help is appreciated :)

Upvotes: 3

Views: 3296

Answers (5)

Kevin Zhu
Kevin Zhu

Reputation: 2836

A forkIO safe version similar to Carl's answer using STM is

import Control.Concurrent.STM
import Control.Monad (replicateM_)
import Control.Monad(forever)

makeCounter :: IO (IO Int)
makeCounter = do
  var <- newTVarIO 0
  return $ do
      atomically $ do
          value <- readTVar var
          modifyTVar var (+1)
          readTVar var

-- a version that only counts from 1 to 10
main1:: IO ()
main1 = do
  counter <- makeCounter
  replicateM_ 10 $ counter >>= print


-- a version that counters forever
main2 :: IO ()
main2 = do
  counter <- makeCounter
  forever $ do
      x<- counter
      print x

main :: IO ()
main = do
  counter <- makeCounter
  tick<- counter
  tick<- counter
  print tick  -- 2

Reference:

  1. Mutable closures in Haskell and nested IO

  2. An EXERCISE from STM tutorial

  3. Mutable State in Haskell

Upvotes: 0

Ed&#39;ka
Ed&#39;ka

Reputation: 7140

For a simple infinite counter just use recursion:

counter n = do print n
               counter (n+1)

main = counter 1

Yet another way to implement tick functionality without using mutable state is to mix State and IO monad using monad transformers:

import Control.Monad.State

type Ticking a = StateT Int IO a

tick :: Ticking ()
tick = do
     modify succ
     get >>= liftIO . print

getCounterValue :: Ticking Int
getCounterValue = get

Then you can use it to create 'ticking' IO functions (with nuisance: IO functions here need to be prefixed with liftIO since it is now Ticking a monad not IO a):

ticking :: Ticking ()
ticking = do
        liftIO $ putStrLn "Starting"
        tick
        tick
        c <- getCounterValue
        liftIO $ do
            putStrLn ("Finished at " ++ show c)
            putStrLn "Press any Enter to start infinite counter"
            getChar
        forever tick

Which can be converted into 'normal' IO using runStateT (with initial counter value):

startTicking :: Ticking a -> Int -> IO a
startTicking = evalStateT

So:

main :: IO ()
main = startTicking ticking 0

Upvotes: 1

luqui
luqui

Reputation: 60463

There are a couple ways to do this without using a mutable cell. You already did it with your second attempt, there's just a little error. You need to pass an initial value to the tick' function, not "set it" (haskell does not have an idea of assigning variables -- only definitions. If the line x = y appears, x will be y for its entire lifetime).

tick = tick' 0
    where ...

The counter = 0 line is not doing anything; it is defining a name that is never used. The counter used in the tick' function is bound as one of its arguments (and shadows the one defined to be 0). Take some time to stare at it with that in mind, see if that makes sense.

There is a nice "higher order" way we can do this too. Essentially we want to run the infinitely long block of code:

do
    print 0
    print 1
    print 2
    ...

There is a function called sequence :: [IO a] -> IO [a] (see caveat below) that will take a list of actions and construct an action. So if we can construct the list [print 0, print 1, print 2, ...] then we can pass it to sequence to build the infinitely long block we are looking for.

Take note here, this is a very important concept in Haskell: [print 0, print 1, print 2] does not print those three numbers then construct the list [0,1,2]. Instead it is itself a list of actions, whose type is [IO ()]. Making the list does nothing; it is only when you bind an action to main that it will be executed. For example, we might say:

main = do
    let xs = [putStrLn "hello", getLine >> putStrLn "world"]
    xs !! 0
    xs !! 0
    xs !! 1
    xs !! 1
    xs !! 0

This would twice print hello, twice get a line and print world after each, then once print hello again.

With that concept, it is easy to build the list of actions [print 0, print 1, ...] with a list comprehension:

main = sequence [ print x | x <- [0..] ]

We can simplify a bit:

main = sequence (map (\x -> print x) [0..])
main = sequence (map print [0..])

So map print [0..] is the list of actions [print 0, print 1, ...] we were looking for, then we just pass that to sequence which chains them together.

This pattern of sequence is common, and has its own mapM:

mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f xs = sequence (map f xs)

Thus:

main = mapM print [0..]

About as simple as you could want.

One note about performance: since we are not using the output of these functions, we should be using sequence_ and mapM_, with trailing underscores, which are optimized for that purpose. Usually this wouldn't matter in a Haskell program because of garbage collection, but in this particular use case is kind of a special case because of various subtleties. You'll find that without the _s, the memory usage of your program gradually grows as the list of results (in this case [(),(),(),...]) is constructed but never used.

Caveat: I have given the type signatures of sequence and mapM specialized to IO, not a general monad, so that the reader does not have to learn about the orthogonal concepts of actions having types and typeclasses at the same time.

Upvotes: 7

rampion
rampion

Reputation: 89053

Your second implementation is really close!

tick = tick'
       where 
           counter = 1
           tick' counter | counter > 0 = do putStrLn (show counter)
                                            tick' (counter + 1)
                         | otherwise = tick

Let's look at the errors for this:

Couldn't match expected type `IO b0' with actual type `a0 -> IO b0'
    In the expression: tick'

Let's add some types to make sure we're getting what we want.

tick is an IO action. We don't care what value the action encapsulates, since the whole point of it is to run forever.

tick :: IO a

Now our error is:

Couldn't match expected type `IO a' with actual type `a0 -> IO b0'
    In the expression: tick'

Well, that's pretty much the same, no help there. Let's keep going.

tick' is a function that takes some integer and returns an IO action that prints the integer and repeats tick' on the next value. Again, we don't care what value the action encapsulates, since it runs forever.

tick' :: Int -> IO b

Wait, now that error makes sense! We defined tick = tick', but the two things have fundamentally different types. One is an action (tick) one is a function that returns an action (tick'). All we need to do is give tick' some value to get the action, so let's do that.

You'd tried to do that by saying where counter = 1 but all that did is define counter as 1 within the statement tick = tick', and since counter isn't mentioned there, it wasn't used.

When you said tick' counter | ... =, you weren't referring to the same counter as on the line above. There, you were defining another variable called counter that was only in scope within the definition of tick'.

So now our code looks like:

tick :: IO a
tick = tick' 1
       where 
           tick' :: Int -> IO b
           tick' counter | counter > 0 = do putStrLn (show counter)
                                            tick' (counter + 1)
                         | otherwise = tick

If we try to compile it, ghc doesn't complain, and if we try it out in ghci it runs as desired:

% ghci
ghci> :l Tick.hs
Ok, modules loaded: Tick.
ghci> tick
1
2
3
...
25244
^C
Interrupted
ghci>

Upvotes: 2

Carl
Carl

Reputation: 27003

Well, let's go back to basics. What you want appears to be an IO action that when bound, prints and increments a counter? I'm going to work from that assumption.

The first thing you need is some mutable cell, since you're using the same action each time. It needs to have something mutable inside it to do something different each time it's used. I'd go with an IORef for this case.

But keeping that IORef hidden is a bit tricky. Especially since globals are bad. The best way to do it is create the IO action from inside another IO action, and then close over the IORef. Doing so gives you something like this:

import Data.IORef

mkCounter :: IO (IO ())
mkCounter = do
    ref <- newIORef 0
    return $ do
        counter <- readIORef ref
        print counter
        writeIORef ref $ counter + 1

This can be used by doing something like this:

main = do
    tick <- mkCounter
    tick
    tick
    tick

Upvotes: 4

Related Questions