Creating state with IORef

In my previous blog post, I wrote about the State Monad and how it cannot be used to create a stateful counter. Fortunately, there are many ways and additions in Haskell (like special libraries, etc.) that can be used to create state the way it is meant to be.

In this blog post, I will demonstrate IORef, which is a way to define mutable entities (variables) in Haskell. Immutable is something that cannot change. Mutable is something that can change. So, a mutable variable is a variable whose value can change. Thus, it constitutes a perfect way to keep state and it also makes a perfect counter, whose value can be, for example, increased by one, each time it is accessed.

The following program gives us an introduction to IORef. newIORef takes an initial value and creates an IORef with that value. modifyIORef takes an exisiting IORef and a function and applies the function to the IORef, thus producing a new IORef that replaces the old IORef. So, IORef functions like any variable in imperative programming.

In the following program, the IORef ref first stores the value 0. Then, using the fucntion (+1), ref stores the value +1. Then, using the function (+5), ref stores the value 6. And after that, using the function (*2), ref stores the value 12.

module Main where

import Data.IORef

main :: IO ()
main  =
   do
      putStrLn "Program begins." 

      ref <- newIORef 0
      readIORef ref >>= print

      modifyIORef ref (+1)
      readIORef ref >>= print

      modifyIORef ref (+5)
      modifyIORef ref (*2)
      readIORef ref >>= print

      putStrLn "Program ends."

The previous program produces the following output:

Program begins.
0
1
12
Program ends.

The following program will achieve what we wanted to do all along: create a stateful counter. Indeed, startCounter creates and returns an IORef that initially stores the value 0. Each time increaseCounter is caled with the IORef as argument, it increments the IORef by 1 and it displays its new value. Although the statement remains the same (increaseCounter counter), the output is different (each time is 1 more than the previous output). This behavior is stateful behavior, something that is certainly a side effect and an abnormality in pure functional programming. But here, it was this exactly that we were trying to achieve.

module Main where

import Data.IORef

startCounter :: IO (IORef Int)
startCounter  =
   do
      ref <- newIORef 0
      return (ref)

increaseCounter        :: IORef Int -> IO ()
increaseCounter counter =
   do
      modifyIORef counter (+1)
      readIORef counter >>= print

main :: IO ()
main  =
   do
      putStrLn "Program begins." 

      counter <- startCounter

      increaseCounter counter
      increaseCounter counter
      increaseCounter counter

      putStrLn "Program ends."

The previous program produces the following output:

Program begins.
1
2
3
Program ends.

As an added bonus, I thought I would present the four statements in the main program’s do section using the monadic notation instead of the do notation. So, the following program is equivalent to the previous program. The only thing that changes is that the statements in the main program use the monadic notation instead of the do notation. This will help the reader understand in general a little bit better the correspondence and equivalence between the monadic notation and the do notation.

module Main where

import Data.IORef

startCounter :: IO (IORef Int)
startCounter  =
   do
      ref <- newIORef 0
      return (ref)

increaseCounter        :: IORef Int -> IO ()
increaseCounter counter =
   do
      modifyIORef counter (+1)
      readIORef counter >>= print

main :: IO ()
main  =
   do
      putStrLn "Program begins."
 
      startCounter >>= (\counter -> increaseCounter counter >>= (\_ -> increaseCounter counter >>= (\_ -> increaseCounter counter)))

      putStrLn "Program ends."

The previous program is equivalent to the second program and thus produces exactly the same output:

Program begins.
1
2
3
Program ends.
Advertisements

About Dimitrios Kalemis

I am a systems engineer specializing in Microsoft products and technologies. I am also an author. Please visit my blog to see the blog posts I have written, the books I have written and the applications I have created. I definitely recommend my blog posts under the category "Management", all my books and all my applications. I believe that you will find them interesting and useful. I am in the process of writing more blog posts and books, so please visit my blog from time to time to see what I come up with next. I am also active on other sites; links to those you can find in the "About me" page of my blog.
This entry was posted in Development. Bookmark the permalink.