Monads do not commute in general, but you can provide all particular cases, that you need:
{-# LANGUAGE MultiParamTypeClasses #-}
import Control.Monad
class (Monad m, Monad n) => Swappable m n where
swap :: m (n a) -> n (m a)
instance Swappable [] IO where
swap = sequence
instance Swappable Maybe IO where
swap Nothing = return Nothing
swap (Just mx) = fmap return mx
cut :: Swappable m n => m (n (m a)) -> n (m a)
cut = liftM join . swap
squash :: Swappable m n => n (m (n (m a))) -> n (m a)
squash = (>>= cut)
An example:
x :: IO [IO [()]]
x = return $ map (\s -> putStrLn s >> return [()]) ["ab","c"]
y :: IO (Maybe (IO (Maybe ())))
y = return $ Just $ putStrLn "z" >> return (Just ())
main = squash x >> squash y
prints
ab
c
z
EDIT
You can avoid defining new typeclass by providing an instance of Traversable
(there are instances for Maybe
and []
in Data.Traversable
):
import Data.Traversable as T
import Control.Monad
cut :: (Traversable t, Monad t, Monad m) => t (m (t a)) -> m (t a)
cut = liftM join . T.sequence
squash :: (Traversable t, Monad t, Monad m) => m (t (m (t a))) -> m (t a)
squash = (>>= cut)