The IORef type provides mutable references in the IO Monad. It allows Haskell to perform computations with mutability like imperative languages.
Module: Data.IORef
Function / Type | Signature | Description | |
---|---|---|---|
Types | |||
data IORef a | |||
Functions | |||
newIORef | :: | a -> IO (IORef a) | Create new IORef |
readIORef | :: | IORef a -> IO a | Read IORef |
writeIORef | :: | IORef a -> a -> IO () | Update IORef |
modifyIORef | :: | IORef a -> (a -> a) -> IO () | Apply a function to the value in the IORef and update it. |
{- ===== Check The Types ====== -}
> import Data.IORef
>
> :info IORef
newtype IORef a
= GHC.IORef.IORef (GHC.STRef.STRef GHC.Prim.RealWorld a)
-- Defined in ‘GHC.IORef’
instance Eq (IORef a) -- Defined in ‘GHC.IORef’
>
> :t newIORef
newIORef :: a -> IO (IORef a)
>
> :t readIORef
readIORef :: IORef a -> IO a
>
> :t modifyIORef
modifyIORef :: IORef a -> (a -> a) -> IO ()
>
{- === Experiment 1 - IORef within the REPL ========== -}
{- | Extract IO a -> a is only possible in the REPL due to it be inside an IO monad. -}
> cell <- newIORef (100.0 :: Double)
> :t cell
cell :: IORef Double
>
> readIORef cell
100.0
>
> :t readIORef cell
readIORef cell :: IO Double
>
-- Apply a function to value returned from reading the cell
---------------------------------------------------------------
> fmap ((+)100) $ readIORef cell
200.0
>
> (*3) <$> ((+)100) <$> readIORef cell
600.0
>
> (+5) <$> (*3) <$> ((+)100) <$> readIORef cell
605.0
>
> (+5) . (*3) . ((+)100) <$> readIORef cell
605.0
>
> fmap ((+5) . (*3) . ((+)100)) $ readIORef cell
605.0
>
-- Update
------------------------------------------------------------
> :t writeIORef
writeIORef :: IORef a -> a -> IO ()
>
> writeIORef cell 500.322
> readIORef cell
500.322
>
> fmap ((+5) . (*3) . ((+)100)) $ readIORef cell
1805.966
>
-- Modify
----------------------------------------------------------
> writeIORef cell 200.0
> readIORef cell
200.0
>
> :t modifyIORef
modifyIORef :: IORef a -> (a -> a) -> IO ()
>
> modifyIORef cell (\x -> 3.0 * x )
> readIORef cell
600.0
> modifyIORef cell (\x -> 3.0 * x )
> readIORef cell
1800.0
> modifyIORef cell (\x -> 3.0 * x )
> readIORef cell
5400.0
>
Example: Counter.
import Data.IORef
:{
counter :: IO (IORef Int)
counter = newIORef 0
:}
:{
incrCounter :: IORef Int -> IO ()
incrCounter cell = modifyIORef cell (\x -> x + 1)
:}
:{
showCounter cell = do
value <- readIORef cell
putStrLn $ "The counter value is " ++ show value
:}
> :t showCounter
showCounter :: Show a => IORef a -> IO ()
>
> counter >>= showCounter
The counter value is 0
>
:{
testCounter1 c = do
incrCounter c
incrCounter c
incrCounter c
showCounter c
:}
> :t testCounter1
testCounter1 :: IORef Int -> IO ()
>
> testCounter1 =<< counter
The counter value is 3
>
> showCounter =<< counter
The counter value is 0
>
> counter >>= showCounter
The counter value is 0
>
> c <- counter
> :t c
c :: IORef Int
>
> showCounter c
The counter value is 0
> incrCounter c
> showCounter c
The counter value is 1
>
> incrCounter c >> showCounter c
The counter value is 2
> incrCounter c >> showCounter c
The counter value is 3
> incrCounter c >> showCounter c
The counter value is 4
> incrCounter c >> showCounter c
The counter value is 5
> incrCounter c >> showCounter c
The counter value is 6
> incrCounter c >> showCounter c
The counter value is 7
> incrCounter c >> showCounter c
The counter value is 8
>
import qualified Data.IORef as IORef
:{
makeCounter :: Int -> IO (IO Int)
makeCounter init = do
c <- IORef.newIORef init
let incCounter = do
a <- IORef.readIORef c
IORef.writeIORef c (a + 1)
return a
return incCounter
:}
> counter <- makeCounter 0
counter :: IO Int
> counter
0
it :: Int
> counter
1
it :: Int
> counter
2
it :: Int
> counter
3
it :: Int
> counter
4
it :: Int
> counter
5
it :: Int
> counter
6
it :: Int
>
import Data.IORef
import Control.Monad (forever, forM_)
:{
sumList :: Num a => [a] -> IO a
sumList xs = do
acc <- newIORef 0
forM_ xs $ \x -> modifyIORef acc (\val -> val + x)
result <- readIORef acc
return result
:}
> :t sumList
sumList :: Num a => [a] -> IO a
>
> sumList [1, 2, 3, 4, 5, 6]
21
> sumList [1, 2, 3, 4, 5, 6, 10]
31
> sumList [1, 2, 3, 4, 5, 6, 10, 11]
42
>
> sumList [1, 2, 3.23, 4.0, 5, 6, 10, 11]
42.230000000000004
>
:set +s
--- SumList simplified
---
:{
sumList :: Num a => [a] -> IO a
sumList xs = do
acc <- newIORef 0
forM_ xs $ \x -> modifyIORef acc (\val -> val + x)
readIORef acc
:}
> sumList [1, 2, 3, 4, 5, 6]
21
> sumList [1, 2, 3.34, 4.7567, 5, 6]
22.0967
>
{- | ========== Testing Space Leaks ============= -}
--
> sumList [1..10000]
50005000
(0.03 secs, 3,770,264 bytes)
>
> sumList [1..10000000]
50000005000000
(6.70 secs, 3,696,483,880 bytes)
>
-- WARNING: Don't try it or the computer may freeze because it consumes a huge amount of memory
--
> sumList [1..1000000000]
fSegmentation fault -- Stop with linux emergency keys - Ctrl + SysRq + f
-- Solution to the space leak issue: Use the strict version of modifyIORef ( modifyIORef')
--
:{
sumList' :: Num a => [a] -> IO a
sumList' xs = do
acc <- newIORef 0
forM_ xs $ \x -> modifyIORef' acc (\val -> val + x)
result <- readIORef acc
return result
:}
> sumList' [1..10000]
50005000
(0.03 secs, 3,120,456 bytes)
-- This implementation is faster.
> sumList' [1..10000000]
50000005000000
(3.64 secs, 3,040,086,304 bytes)
>
Example: Ask the number for a number and display the sum in a infinite loop.
import Data.IORef
import Control.Monad (forever, forM_)
:{
sumLoop :: IO ()
sumLoop = do
cell <- newIORef (0 :: Int)
forever $ do putStr "Enter the next number to sum: "
nextNum <- fmap read getLine :: IO Int
modifyIORef cell (\value-> value + nextNum)
sumval <- readIORef cell
putStrLn $ "sum = " ++ show sumval
:}
> sumLoop
Enter the next number to sum: 100
sum = 100
Enter the next number to sum: 200
sum = 300
Enter the next number to sum: 300
sum = 600
Enter the next number to sum: 45
sum = 645
Enter the next number to sum: 78
sum = 723
Enter the next number to sum: -458
sum = 265
Enter the next number to sum: 200
sum = 465
Enter the next number to sum: 300
sum = 765
Enter the next number to sum: ^CInterrupted. --- Type Ctrl+C to exit the Loop
>
import Data.IORef
import Control.Concurrent (threadDelay)
import Control.Monad (forever)
delay1Second = 1000000 -- 1 million us = 1 second
:{
runLoop = do
count <- newIORef 0
forever $ do val <- readIORef count
putStrLn $ "Counter is " ++ show val
modifyIORef count (+1)
threadDelay delay1Second
:}
> runLoop
Counter is 0
Counter is 1
Counter is 2
Counter is 3
Counter is 4
Counter is 5
Counter is 6
Counter is 7
Counter is 8
Counter is 9
Counter is 10
Counter is 11
Counter is 12
Counter is 13
Counter is 14
Counter is 15
Counter is 16
Counter is 17
... ... ...
The code below simulates an object counter that has an internal integer state. The functions or IO actions incrementCounter, decrementcounter and getCounter simulate the object’s methods.
import qualified Data.IORef as IORef
:{
data Counter = Counter { incrementCounter :: IO ()
, decrementCounter :: IO ()
, getCounter :: IO Int
}
:}
:{
newCounter :: IO Counter
newCounter = do
counter <- IORef.newIORef 0
return $ Counter { incrementCounter = IORef.modifyIORef counter (+1)
, decrementCounter = IORef.modifyIORef counter (+(-1))
, getCounter = IORef.readIORef counter
}
:}
Running:
> counter <- newCounter
counter :: Counter
> getCounter counter
0
it :: Int
>
> incrementCounter counter
it :: ()
> getCounter counter
1
it :: Int
> incrementCounter counter
it :: ()
> getCounter counter
2
it :: Int
> incrementCounter counter >> getCounter counter
3
it :: Int
> incrementCounter counter >> getCounter counter
4
it :: Int
>
> decrementCounter counter >> getCounter counter
3
it :: Int
> decrementCounter counter >> getCounter counter
2
it :: Int
> decrementCounter counter >> getCounter counter
1
it :: Int
> decrementCounter counter >> getCounter counter
0
it :: Int
>
The ST monad is used to perform local mutability. The mutability happens only inside the function.
Modules:
Functions:
Function | Signature | Description | |
---|---|---|---|
Module Data.STRef | |||
newSTRef | :: | a -> ST s (STRef s a) | Create a new STRef in the current state thread |
readSTRef | :: | STRef s a -> ST s a | Read the value of an STRef |
writeSTRef | :: | STRef s a -> a -> ST s () | Write a new value into an STRef |
modifySTRef | :: | STRef s a -> (a -> a) -> ST s () | Mutate the contents of an STRef. |
Module Control.Monad.ST | |||
runST | :: | (forall s. ST s a) -> a | Extract content |
Monad and Functor signatures | |||
(>>=) | :: | ST s a -> (a -> ST s b) -> ST s b | Monad bind operator |
return | Monad return function | ||
(>>) | :: | ST s a -> ST s b -> ST s b | Monad (>>) operator |
fmap | :: | (a -> b) -> ST s a -> ST s b | Functor operator |
Example 1: Sum all list elements.
import Control.Monad.ST
import Data.STRef
import Control.Monad (forM_)
:{
sumST :: Num a => [a] -> a
sumST xs = runST $ do -- Extract value from ST Monad
n <- newSTRef 0 -- Create an STRef mutable reference (aka variable)
forM_ xs $ \x -> do -- For each element of xs, add it to n
modifySTRef n (+x)
readSTRef n -- read value from mutable reference n and return it
:}
>
> sumST [1, 2, 3, 4, 5, 6]
21
> sum [1, 2, 3, 4, 5, 6]
21
>
> sumST [1..100]
5050
>
> sumST [1..1000000]
500000500000
>
Example: Alternative fold implementation in a imperative-like way.
:{
foldST :: (acc -> x -> acc) -> acc -> [x] -> acc
foldST step acc0 xs =
runST $ do accum <- newSTRef acc0
forM_ xs $ \x -> modifySTRef accum (\a -> step a x)
readSTRef accum
:}
> foldl (\acc x -> 10*acc + x) 0 [1, 2, 3, 4, 5]
12345
>
> foldl (\acc x -> 10*acc + x) 0 [1, 2, 3, 4, 5, 6]
123456
>
> foldST (\acc x -> 10*acc + x) 0 [1, 2, 3, 4, 5]
12345
> foldST (\acc x -> 10*acc + x) 0 [1, 2, 3, 4, 5, 6]
123456
>
It is equivalent to the code below in Python. This function is a pure fucntion like foldST despite it has local mutability.
def foldl(step, acc0, xs):
accum = acc0
for x in xs:
accum = step(accum, x)
return accum
>>> def foldl(step, acc0, xs):
... accum = acc0
... for x in xs:
... accum = step(accum, x)
... return accum
...
>>>
>>> foldl(lambda a, x: 10 * a + x, 0, [1, 2, 3, 4, 5])
12345
>>>
>>> foldl(lambda a, x: 10 * a + x, 0, [1, 2, 3, 4, 5, 6])
123456
>>>