As a complete Haskell noob, I'm wondering about the push in this snippet:
startDownload = do
file <- atomically $ do
f <- getQueuedFile
push f currentDownLoads
return f
startDownloadThread file
How can that push be non-side-effecting and still do something sensible? If the snippet is replayed, won't multiple pushes happen? This may be a question only joeyh can answer, if this is something specific to his code base...
Naively, I would write something like (again, my Haskell not so good):
startDownload = do
file <- atomically $ getQueuedFile
push f currentDownLoads
startDownloadThread file
You can think about your second version as without side-effects too. It's just in IO monad. Actually, all IO is kinda pure, just (IO smth) values fly behind the scenes. All dirty unpure things happen in runtime.
And in the first snippet it is in STM monad, and you can do, for example
modifyTVar :: TVar a -> (a -> a) -> STM ()
You should understand monads more to grok it, it's quite beautiful.
push doesn't have to be non-side-effecting.
You are allowed to modify shared state, as long as it is a TVar (shared memory that supports transactions).
Anything that happens in the STM monad can be undone if something is wrong. If you for example write to a TVal but another thread was doing it at the same time, one of the threads won't have its changes go through until the transaction is retried. Of course this is all invisible to the person using STM.
Naively, I would write something like (again, my Haskell not so good):