Action to transmit queued messages over a socket I have this action and it works but looks rather clunky to me. Is there any way I could improve that?

transMit :: Serialize a =&gt; Socket -&gt; POSIXTime -&gt; KEY -&gt; a -&gt; TPSQ -&gt; TMap a -&gt; IO ()
transMit s time key newmsgs q m = do
     loopAction &lt;- atomically $ do
                       mT &lt;- readTVar m
                       qT &lt;- readTVar q
                       let (a, b) = key  -- extract IPv4 address a and protocolID b from KEY
                       let mT' = Map.delete key mT
                       let qT' = PSQ.delete key qT
                       writeTVar q (PSQ.insert key time qT')
                       writeTVar m (Map.insert key [newmsgs] mT')
                       case b of
                              11 -&gt; return $ case Map.lookup key mT of
                                                  Nothing -&gt; return ()
                                                  Just messages -&gt; sendq s (B.snoc (S.encode messages) b ) (show a) 4711
                              72 -&gt; return $ case Map.lookup key mT of
                                                  Nothing -&gt; return ()
                                                  Just messages -&gt; sendqr s (B.snoc (S.encode messages) b ) (show a) 4711

     loopAction
I'm not familiar with the libraries and types you're using here, but I see a few general Haskell things:


Instead of loopAction &lt;- atomically $ ...; loopAction, you can use join $ atomically $ .... This removes the need for the entire outer do block, simplifying the blocks/indentation.
You're doing nearly-identical operations on m and q, just with different collection types. I would attempt to extract these to a higher-order function (e.g. one possible abstraction is modifyTVar, which doesn't exist in the library, but you can see how to write it analogously to modifyMVar), and perhaps define an ad-hoc typeclass to make Map and PSQ look the same.
Your case b of ... has two nearly identical branches! Unless there's some non-obvious polymorphism, then just combine them:

let send = case b of
    11 -&gt; sendq
    72 -&gt; sendqr
return $ case Map.lookup key mT of
    Nothing -&gt; return ()
    Just messages -&gt; send s (B.snoc (S.encode messages) b) (show a) 4711

let mT' = Map.delete key mT is redundant because the following insert will always overwrite that key anyway; just use Map.insert key [newmsgs] mT and never define mT'.
Assuming you're using Data.PSQueue, and the key already exists in the queue, you can use PSQ.adjust instead of a delete followed by an insert.


Taking all of my own advice except the last:

transMit :: Serialize a =&gt; Socket -&gt; POSIXTime -&gt; KEY -&gt; a -&gt; TPSQ -&gt; TMap a -&gt; IO ()
transMit s time key newmsgs q m =
    join $ atomically $ do
        modifyTVar_ q $ (PSQ.insert key time . PSQ.delete key)
        foo &lt;- modifyTVar m $ \mT -&gt; (Map.insert key [newmsgs] mT, Map.lookup key mT)
        let (address, protocol) = key
        let send = case protocol of
            11 -&gt; sendq
            72 -&gt; sendqr
        return $ case foo of
            Nothing -&gt; return ()
            Just messages -&gt; send s (B.snoc (S.encode messages) protocol) (show address) 4711

modifyTVar :: TVar a -&gt; (a -&gt; IO (a, b)) -&gt; IO b
modifyTVar var f = do
    x &lt;- readTVar var
    (x', r) &lt;- f x
    writeTVar var x'
    return r
modifyTVar_ :: TVar a -&gt; (a -&gt; IO a) -&gt; IO ()
modifyTVar_ var f = do
    x &lt;- readTVar var
    writeTVar var (f x)


Further points about what I did:


Note I called a variable foo because I don't know what it makes sense to name it in your application, not because that's a good name.
I went and defined both modifyTVar and modifyTVar_; the latter is not needed but allows the action on q to be defined as a simple function composition. This is perhaps an excess of specialized utility functions, but I feel it is reasonable because the experienced Haskell programmer will understand what they do simply from seeing their names.
I renamed a and b so the variable names actually communicate what they are; this is better than writing a comment to explain them, and so I removed the comment.
I put join $ atomically $ do on a separate line so as to catch the reader's eye that this is not just a plain do block.