Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`forever`: How to forward information to next iteration?

There is a thread waiting for new input in a queue to safe it to the file system. It also creates backup copies. The sscce looks like this:

import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import Data.Time.Clock.POSIX

main :: IO ()
main = do
    contentQueue <- atomically $ newTQueue
    _ <- forkIO $ saveThreadFunc contentQueue
    forever $ do
        line <- getLine
        atomically $ writeTQueue contentQueue line

saveThreadFunc :: TQueue String -> IO ()
saveThreadFunc queue = forever $ do
    newLine <- atomically $ readTQueue queue
    now <- round `fmap` getPOSIXTime :: IO Int
    writeFile "content.txt" newLine
    -- todo: Backup no more than once every 86400 seconds (24 hours).
    backupContent now newLine

backupContent :: Int -> String -> IO ()
backupContent t = writeFile $ "content.backup." ++ show t

Now it would be great if the backup would not be written more than once every 24 hours. In imperative programming I would probably use a mutable int lastBackupTime inside the "forever loop" in saveThreadFunc. How can the same effect be achieved in Haskell?

like image 492
Tobias Hermann Avatar asked Feb 12 '15 12:02

Tobias Hermann


1 Answers

How about Control.Monad.Loops.iterateM_? This is slightly neater as it avoids explict recursion.

iterateM_ :: Monad m => (a -> m a) -> a -> m b

saveThreadFunc :: TQueue String -> Int -> IO ()
saveThreadFunc queue = iterateM_ $ \lastBackupTime -> do
  newLine <- atomically $ readTQueue queue
  now <- round `fmap` getPOSIXTime :: IO Int
  writeFile "content.txt" newLine
  let makeNewBackup = now >= lastBackupTime + 86400
  when makeNewBackup (backupContent now newLine)
  return (if makeNewBackup then now else lastBackupTime)
like image 193
Tom Ellis Avatar answered Sep 30 '22 14:09

Tom Ellis