Timers in Haskell

Our first example demonstrates the use of timers in Haskell. We often want to execute code at some point in the future, or repeatedly at some interval. Haskell’s threadDelay function and the async library can help us achieve these tasks easily.

import Control.Concurrent (threadDelay)
import Control.Concurrent.Async (async, cancel)
import Control.Monad (void)
import System.IO (hFlush, stdout)

main :: IO ()
main = do
    -- Timers represent a single event in the future. You
    -- tell the timer how long you want to wait, and it
    -- will execute after that time. This timer will wait 2 seconds.
    putStrLn "Starting Timer 1"
    threadDelay (2 * 1000000)  -- 2 seconds
    putStrLn "Timer 1 fired"

    -- If you just wanted to wait, you could have used
    -- threadDelay directly. One reason to use async is
    -- that you can cancel the timer before it fires.
    -- Here's an example of that.
    putStrLn "Starting Timer 2"
    timer2 <- async $ do
        threadDelay 1000000  -- 1 second
        putStrLn "Timer 2 fired"

    cancel timer2
    putStrLn "Timer 2 stopped"

    -- Give timer2 enough time to fire, if it ever
    -- was going to, to show it is in fact stopped.
    threadDelay (2 * 1000000)  -- 2 seconds

    -- Ensure all output is flushed
    hFlush stdout

To run the program, save it as timers.hs and use runhaskell:

$ runhaskell timers.hs
Starting Timer 1
Timer 1 fired
Starting Timer 2
Timer 2 stopped

The first timer will fire ~2s after we start the program, but the second should be stopped before it has a chance to fire.

In this Haskell version:

  1. We use threadDelay to create delays. The argument to threadDelay is in microseconds, so we multiply by 1,000,000 to get seconds.

  2. Instead of channels, we use the async library to create asynchronous actions that can be cancelled.

  3. The cancel function is used to stop the second timer before it fires.

  4. We use putStrLn for output and hFlush stdout to ensure all output is displayed immediately.

This example demonstrates basic timer functionality in Haskell, including creating timed events and cancelling them before they occur.