Skip to content

Latest commit

 

History

History
640 lines (511 loc) · 15.3 KB

Mutable_References.org

File metadata and controls

640 lines (511 loc) · 15.3 KB

Mutable References

Mutable References

IORef - Mutable References Inside IO Monad

Overview

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 / TypeSignatureDescription
Types
data IORef a
Functions
newIORef::a -> IO (IORef a)Create new IORef
readIORef::IORef a -> IO aRead 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.

Examples

Simple IORef example in Haskell REPL:

{- ===== 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
> 

Stateful Function

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
> 

Example: Sum list elements in a imperative way using IORef.

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
> 

       

Infinite loop with delay

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
... ... ...
    

Simulating objects with closures

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
> 

ST Monad - Mutable References Inside Pure Functions

Overview

The ST monad is used to perform local mutability. The mutability happens only inside the function.

Modules:

Functions:

FunctionSignatureDescription
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 aRead 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) -> aExtract content
Monad and Functor signatures
(>>=)::ST s a -> (a -> ST s b) -> ST s bMonad bind operator
returnMonad return function
(>>)::ST s a -> ST s b -> ST s bMonad (>>) operator
fmap::(a -> b) -> ST s a -> ST s bFunctor operator

Examples

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
>>>