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 IORef
s, 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.