Home > OS >  Immutability of haskell dequeue
Immutability of haskell dequeue

Time:12-31

I have an application that needs to process some items on a queue. Sometimes taking off items, sometimes putting on new items. I am confused about how to achieve this given the immutability of haskell data strucutres. Here is my experimental code

module Main where

import Data.Dequeue as D
import Data.Time
import Control.Concurrent (threadDelay)
import Control.Monad (forever)


data ScheduledCall = ScheduledCall {
        not_before :: UTCTime
    ,   apiParameters :: String
}

sleep :: Int -> IO ()
sleep n = threadDelay (n * 1000 * 1000)

main :: IO ()
main = do
    let workQ :: D.BankersDequeue ScheduledCall
        workQ = D.empty
    now <- getCurrentTime
    forever $ do
        let workQ' = D.pushBack workQ (ScheduledCall now "first")
        -- possibly do something with the items
        let len = length workQ'
        let workQ = workQ'
        putStrLn $ "Length "    (show len)
        sleep 5

With this code I expect to see the dequeue growing by 1 on each iteration, but it is not. This implies the structure remains immutable.

How do I use the dequeue through different iterations?

CodePudding user response:

You could use recursion.

main = do
   now <- getCurrentTime
   let loop workQ = do
          let workQ' = D.pushBack workQ (ScheduledCall now "first")
          -- possibly do something with the items
          let len = length workQ'
          putStrLn $ "Length "    (show len)
          sleep 5
          loop workQ'   -- recurse with the new queue
   loop D.empty  -- start the loop with an initially empty dqueue

There are more advanced (and complex) techniques to model mutability, like using IORefs, or the ST/State monads, but for simple cases a basic recursive definition is enough.


Note that you can not "reassign" like you tried to do:

let workQ = workQ'
putStrLn $ "Length "    (show len)
sleep 5

This defines a new variable workQ which is completely unrelated to the other variable with the same name defined above. The scope of the new variable is only the following lines, where it is not used, so it is a useless definition.

You can think of that redefinition like those happening in, say, C :

{
   Dqueue workQ = ... ;
   stuff();
   if (blah) {
      Dqueue workQ = ... ;
      // this does not affect the outer variable
   }
}

Defining a variable with the same name as another one defined in an outer scope is called "shadowing". GHC warns about that (if you enable warnings), since it is usually a mistake.

  • Related