content: 
  -- | Utilities related to Monad and Applicative classes
  --   Mostly for backwards compatability.
  
  module Eta.Utils.MonadUtils
          ( Applicative(..)
          , (<$>)
  
          , MonadFix(..)
          , MonadIO(..)
  
          , liftIO1, liftIO2, liftIO3, liftIO4
  
          , zipWith3M, zipWith3M_, zipWithAndUnzipM
          , mapAndUnzipM, mapAndUnzip3M, mapAndUnzip4M
          , mapAccumLM
          , mapSndM
          , concatMapM
          , mapMaybeM
          , fmapMaybeM, fmapEitherM
          , anyM, allM, orM
          , foldlM, foldlM_, foldrM
          , maybeMapM
          , whenM
          ) where
  
  -------------------------------------------------------------------------------
  -- Imports
  -------------------------------------------------------------------------------
  
  import Eta.Utils.Maybes
  
  import Control.Applicative
  import Control.Monad
  import Control.Monad.Fix
  import Control.Monad.IO.Class
  import Prelude -- avoid redundant import warning due to AMP
  
  -------------------------------------------------------------------------------
  -- Lift combinators
  --  These are used throughout the compiler
  -------------------------------------------------------------------------------
  
  -- | Lift an 'IO' operation with 1 argument into another monad
  liftIO1 :: MonadIO m => (a -> IO b) -> a -> m b
  liftIO1 = (.) liftIO
  
  -- | Lift an 'IO' operation with 2 arguments into another monad
  liftIO2 :: MonadIO m => (a -> b -> IO c) -> a -> b -> m c
  liftIO2 = ((.).(.)) liftIO
  
  -- | Lift an 'IO' operation with 3 arguments into another monad
  liftIO3 :: MonadIO m => (a -> b -> c -> IO d) -> a -> b -> c -> m d
  liftIO3 = ((.).((.).(.))) liftIO
  
  -- | Lift an 'IO' operation with 4 arguments into another monad
  liftIO4 :: MonadIO m => (a -> b -> c -> d -> IO e) -> a -> b -> c -> d -> m e
  liftIO4 = (((.).(.)).((.).(.))) liftIO
  
  -------------------------------------------------------------------------------
  -- Common functions
  --  These are used throughout the compiler
  -------------------------------------------------------------------------------
  
  zipWith3M :: Monad m => (a -> b -> c -> m d) -> [a] -> [b] -> [c] -> m [d]
  zipWith3M _ []     _      _      = return []
  zipWith3M _ _      []     _      = return []
  zipWith3M _ _      _      []     = return []
  zipWith3M f (x:xs) (y:ys) (z:zs)
    = do { r  <- f x y z
         ; rs <- zipWith3M f xs ys zs
         ; return $ r:rs
         }
  
  zipWith3M_ :: Monad m => (a -> b -> c -> m d) -> [a] -> [b] -> [c] -> m ()
  zipWith3M_ f as bs cs = do { _ <- zipWith3M f as bs cs
                             ; return () }
  
  zipWithAndUnzipM :: Monad m
                   => (a -> b -> m (c, d)) -> [a] -> [b] -> m ([c], [d])
  {-# INLINE zipWithAndUnzipM #-}
  -- See Note [flatten_many performance] in TcFlatten for why this
  -- pragma is essential.
  zipWithAndUnzipM f (x:xs) (y:ys)
    = do { (c, d) <- f x y
         ; (cs, ds) <- zipWithAndUnzipM f xs ys
         ; return (c:cs, d:ds) }
  zipWithAndUnzipM _ _ _ = return ([], [])
  
  -- | mapAndUnzipM for triples
  mapAndUnzip3M :: Monad m => (a -> m (b,c,d)) -> [a] -> m ([b],[c],[d])
  mapAndUnzip3M _ []     = return ([],[],[])
  mapAndUnzip3M f (x:xs) = do
      (r1,  r2,  r3)  <- f x
      (rs1, rs2, rs3) <- mapAndUnzip3M f xs
      return (r1:rs1, r2:rs2, r3:rs3)
  
  mapAndUnzip4M :: Monad m => (a -> m (b,c,d,e)) -> [a] -> m ([b],[c],[d],[e])
  mapAndUnzip4M _ []     = return ([],[],[],[])
  mapAndUnzip4M f (x:xs) = do
      (r1,  r2,  r3,  r4)  <- f x
      (rs1, rs2, rs3, rs4) <- mapAndUnzip4M f xs
      return (r1:rs1, r2:rs2, r3:rs3, r4:rs4)
  
  -- | Monadic version of mapAccumL
  mapAccumLM :: Monad m
              => (acc -> x -> m (acc, y)) -- ^ combining funcction
              -> acc                      -- ^ initial state
              -> [x]                      -- ^ inputs
              -> m (acc, [y])             -- ^ final state, outputs
  mapAccumLM _ s []     = return (s, [])
  mapAccumLM f s (x:xs) = do
      (s1, x')  <- f s x
      (s2, xs') <- mapAccumLM f s1 xs
      return    (s2, x' : xs')
  
  -- | Monadic version of mapSnd
  mapSndM :: Monad m => (b -> m c) -> [(a,b)] -> m [(a,c)]
  mapSndM _ []         = return []
  mapSndM f ((a,b):xs) = do { c <- f b; rs <- mapSndM f xs; return ((a,c):rs) }
  
  -- | Monadic version of concatMap
  concatMapM :: Monad m => (a -> m [b]) -> [a] -> m [b]
  concatMapM f xs = liftM concat (mapM f xs)
  
  -- | Monadic version of mapMaybe
  mapMaybeM :: (Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
  mapMaybeM f = liftM catMaybes . mapM f
  
  -- | Monadic version of fmap
  fmapMaybeM :: (Monad m) => (a -> m b) -> Maybe a -> m (Maybe b)
  fmapMaybeM _ Nothing  = return Nothing
  fmapMaybeM f (Just x) = f x >>= (return . Just)
  
  -- | Monadic version of fmap
  fmapEitherM :: Monad m => (a -> m b) -> (c -> m d) -> Either a c -> m (Either b d)
  fmapEitherM fl _ (Left  a) = fl a >>= (return . Left)
  fmapEitherM _ fr (Right b) = fr b >>= (return . Right)
  
  -- | Monadic version of 'any', aborts the computation at the first @True@ value
  anyM :: Monad m => (a -> m Bool) -> [a] -> m Bool
  anyM _ []     = return False
  anyM f (x:xs) = do b <- f x
                     if b then return True
                          else anyM f xs
  
  -- | Monad version of 'all', aborts the computation at the first @False@ value
  allM :: Monad m => (a -> m Bool) -> [a] -> m Bool
  allM _ []     = return True
  allM f (b:bs) = (f b) >>= (\bv -> if bv then allM f bs else return False)
  
  -- | Monadic version of or
  orM :: Monad m => m Bool -> m Bool -> m Bool
  orM m1 m2 = m1 >>= \x -> if x then return True else m2
  
  -- | Monadic version of foldl
  foldlM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
  foldlM = foldM
  
  -- | Monadic version of foldl that discards its result
  foldlM_ :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m ()
  foldlM_ = foldM_
  
  -- | Monadic version of foldr
  foldrM        :: (Monad m) => (b -> a -> m a) -> a -> [b] -> m a
  foldrM _ z []     = return z
  foldrM k z (x:xs) = do { r <- foldrM k z xs; k x r }
  
  -- | Monadic version of fmap specialised for Maybe
  maybeMapM :: Monad m => (a -> m b) -> (Maybe a -> m (Maybe b))
  maybeMapM _ Nothing  = return Nothing
  maybeMapM m (Just x) = liftM Just $ m x
  
  -- | Monadic version of @when@, taking the condition in the monad
  whenM :: Monad m => m Bool -> m () -> m ()
  whenM mb thing = do { b <- mb
                      ; when b thing }
  