    {-# LANGUAGE Trustworthy #-}
    {-# LANGUAGE NoImplicitPrelude #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Control.Monad
    -- Copyright   :  (c) The University of Glasgow 2001
    -- License     :  BSD-style (see the file libraries/base/LICENSE)
    --
    -- Maintainer  :  libraries@haskell.org
    -- Stability   :  provisional
    -- Portability :  portable
    --
    -- The 'Functor', 'Monad' and 'MonadPlus' classes,
    -- with some useful operations on monads.

    module Control.Monad
        (
        -- * Functor and monad classes

          Functor(fmap)
        , Monad((>>=), (>>), return, fail)
        , MonadPlus(mzero, mplus)
        -- * Functions

        -- ** Naming conventions
        -- $naming

        -- ** Basic @Monad@ functions

        , mapM
        , mapM_
        , forM
        , forM_
        , sequence
        , sequence_
        , (=<<)
        , (>=>)
        , (<=<)
        , forever
        , void

        -- ** Generalisations of list functions

        , join
        , msum
        , mfilter
        , filterM
        , mapAndUnzipM
        , zipWithM
        , zipWithM_
        , foldM
        , foldM_
        , replicateM
        , replicateM_

        -- ** Conditional execution of monadic expressions

        , guard
        , when
        , unless

        -- ** Monadic lifting operators

        , liftM
        , liftM2
        , liftM3
        , liftM4
        , liftM5

        , ap

        -- ** Strict monadic functions

        , (<$!>)
        ) where

    import Data.Foldable ( Foldable, sequence_, msum, mapM_, foldlM, forM_ )
    import Data.Functor ( void )
    import Data.Traversable ( forM, mapM, sequence )

    import GHC.Base hiding ( mapM, sequence )
    import GHC.List ( zipWith, unzip, replicate )

    -- -----------------------------------------------------------------------------
    -- Functions mandated by the Prelude

    -- | @'guard' b@ is @'pure' ()@ if @b@ is 'True',
    -- and 'empty' if @b@ is 'False'.
    guard           :: (Alternative f) => Bool -> f ()
    guard True      =  pure ()
    guard False     =  empty

    -- | This generalizes the list-based 'filter' function.

    {-# INLINE filterM #-}
    filterM          :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
    filterM p        = foldr go (return [])
      where
        go x r = do
          flg <- p x
          ys <- r
          return (if flg then x:ys else ys)

    infixr 1 <=<, >=>

    -- | Left-to-right Kleisli composition of monads.
    (>=>)       :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
    f >=> g     = \x -> f x >>= g

    -- | Right-to-left Kleisli composition of monads. @('>=>')@, with the arguments flipped
    (<=<)       :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
    (<=<)       = flip (>=>)

    -- | @'forever' act@ repeats the action infinitely.
    forever     :: (Monad m) => m a -> m b
    {-# INLINE forever #-}
    forever a   = let a' = a >> a' in a'
    -- Use explicit sharing here, as it is prevents a space leak regardless of
    -- optimizations.

    -- -----------------------------------------------------------------------------
    -- Other monad functions

    -- | The 'mapAndUnzipM' function maps its first argument over a list, returning
    -- the result as a pair of lists. This function is mainly used with complicated
    -- data structures or a state-transforming monad.
    mapAndUnzipM      :: (Monad m) => (a -> m (b,c)) -> [a] -> m ([b], [c])
    {-# INLINE mapAndUnzipM #-}
    mapAndUnzipM f xs =  sequence (map f xs) >>= return . unzip

    -- | The 'zipWithM' function generalizes 'zipWith' to arbitrary monads.
    zipWithM          :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]
    {-# INLINE zipWithM #-}
    zipWithM f xs ys  =  sequence (zipWith f xs ys)

    -- | 'zipWithM_' is the extension of 'zipWithM' which ignores the final result.
    zipWithM_         :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m ()
    {-# INLINE zipWithM_ #-}
    zipWithM_ f xs ys =  sequence_ (zipWith f xs ys)

    {- | The 'foldM' function is analogous to 'foldl', except that its result is
    encapsulated in a monad. Note that 'foldM' works from left-to-right over
    the list arguments. This could be an issue where @('>>')@ and the `folded
    function' are not commutative.

    >       foldM f a1 [x1, x2, ..., xm]

    ==

    >       do
    >         a2 <- f a1 x1
    >         a3 <- f a2 x2
    >         ...
    >         f am xm

    If right-to-left evaluation is required, the input list should be reversed.

    Note: 'foldM' is the same as 'foldlM'
    -}

    foldM          :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
    {-# INLINEABLE foldM #-}
    {-# SPECIALISE foldM :: (a -> b -> IO a) -> a -> [b] -> IO a #-}
    {-# SPECIALISE foldM :: (a -> b -> Maybe a) -> a -> [b] -> Maybe a #-}
    foldM          = foldlM

    -- | Like 'foldM', but discards the result.
    foldM_         :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m ()
    {-# INLINEABLE foldM_ #-}
    {-# SPECIALISE foldM_ :: (a -> b -> IO a) -> a -> [b] -> IO () #-}
    {-# SPECIALISE foldM_ :: (a -> b -> Maybe a) -> a -> [b] -> Maybe () #-}
    foldM_ f a xs  = foldlM f a xs >> return ()

    -- | @'replicateM' n act@ performs the action @n@ times,
    -- gathering the results.
    replicateM        :: (Monad m) => Int -> m a -> m [a]
    {-# INLINEABLE replicateM #-}
    {-# SPECIALISE replicateM :: Int -> IO a -> IO [a] #-}
    {-# SPECIALISE replicateM :: Int -> Maybe a -> Maybe [a] #-}
    replicateM n x    = sequence (replicate n x)

    -- | Like 'replicateM', but discards the result.
    replicateM_       :: (Monad m) => Int -> m a -> m ()
    {-# INLINEABLE replicateM_ #-}
    {-# SPECIALISE replicateM_ :: Int -> IO a -> IO () #-}
    {-# SPECIALISE replicateM_ :: Int -> Maybe a -> Maybe () #-}
    replicateM_ n x   = sequence_ (replicate n x)

    -- | The reverse of 'when'.
    unless            :: (Applicative f) => Bool -> f () -> f ()
    {-# INLINEABLE unless #-}
    {-# SPECIALISE unless :: Bool -> IO () -> IO () #-}
    {-# SPECIALISE unless :: Bool -> Maybe () -> Maybe () #-}
    unless p s        =  if p then pure () else s

    infixl 4 <$!>

    -- | Strict version of 'Data.Functor.<$>'.
    --
    -- @since 4.8.0.0
    (<$!>) :: Monad m => (a -> b) -> m a -> m b
    {-# INLINE (<$!>) #-}
    f <$!> m = do
      x <- m
      let z = f x
      z `seq` return z

    -- -----------------------------------------------------------------------------
    -- Other MonadPlus functions

    -- | Direct 'MonadPlus' equivalent of 'filter'
    -- @'filter'@ = @(mfilter:: (a -> Bool) -> [a] -> [a]@
    -- applicable to any 'MonadPlus', for example
    -- @mfilter odd (Just 1) == Just 1@
    -- @mfilter odd (Just 2) == Nothing@

    mfilter :: (MonadPlus m) => (a -> Bool) -> m a -> m a
    {-# INLINEABLE mfilter #-}
    mfilter p ma = do
      a <- ma
      if p a then return a else mzero

    {- $naming

    The functions in this library use the following naming conventions:

    * A postfix \'@M@\' always stands for a function in the Kleisli category:
      The monad type constructor @m@ is added to function results
      (modulo currying) and nowhere else.  So, for example,

    >  filter  ::              (a ->   Bool) -> [a] ->   [a]
    >  filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]

    * A postfix \'@_@\' changes the result type from @(m a)@ to @(m ())@.
      Thus, for example:

    >  sequence  :: Monad m => [m a] -> m [a]
    >  sequence_ :: Monad m => [m a] -> m ()

    * A prefix \'@m@\' generalizes an existing function to a monadic form.
      Thus, for example:

    >  sum  :: Num a       => [a]   -> a
    >  msum :: MonadPlus m => [m a] -> m a

    -}