Mutable references

A TVar is a variable whose value can be changed. Reading a TVar gets its current value, and writing a TVar assigns it a new value.

TVar comes from the stm library. The ‘T’ in both ‘STM’ and ‘TVar’ stands for ‘transactional’, which refers to some impressive thread-safety properties of these variables. But this won’t matter until we fork some threads later on.

import Control.Monad.STM
import Control.Concurrent.STM.TVar

import Data.Foldable (for_)

There are four important functions to remember when working with TVars:

main =
  1. newTVar initializes a new variable. Here we initialize two variables, a = 3 and b = 5.
    a <- atomically (newTVar 3)
    b <- atomically (newTVar 5)

      printVars label =
  1. readTVar returns the variable’s current value.

The printVars function reads and then prints the values of a and b. We will use it repeatedly to watch how these two variables change as the program runs.

          x1 <- atomically (readTVar a)
          x2 <- atomically (readTVar b)
          putStrLn ("a = " ++ show x1 ++ ", b = " ++ show x2
                    ++ " (" ++ label ++ ")")

    printVars "initial values"
  1. writeTVar assigns a new value to a variable. Here we reassign a with a new value of 7.
    atomically (writeTVar a 7)
    printVars "changed a to 7"
  1. modifyTVar' applies a function to a variable’s value. Here we apply the function (* 2) to double the value of b.
    atomically (modifyTVar' b (* 2))
    printVars "doubled b"

Mutable references can be passed as arguments to functions. Here we define two functions:

  • increment increases a variable’s value by one;

  • swap interchanges the values of two variables.

        increment ref = atomically (modifyTVar' ref (+ 1))

        swap ref1 ref2 =
          atomically $
              x1 <- readTVar ref1
              x2 <- readTVar ref2
              writeTVar ref1 x2
              writeTVar ref2 x1

We increment a once, then increment b five times in a for loop.

    increment a
    for_ [1..5] $ \_ ->
        increment b
    printVars "incremented"

And finally, we’ll swap the values of a and b.

    swap a b
    printVars "swapped"

When we run the program, we can see how the values of a and b evolve throughout the program’s execution.

$ runhaskell mutable-references.hs
a = 3, b = 5 (initial values)
a = 7, b = 5 (changed a to 7)
a = 7, b = 10 (doubled b)
a = 8, b = 15 (incremented)
a = 15, b = 8 (swapped)