{-# OPTIONS_GHC -Wall -fwarn-tabs #-}
----------------------------------------------------------------
--                                                  ~ 2010.11.15
-- |
-- Module      :  Control.Monad.Extras
-- Copyright   :  Copyright (c) 2007--2010 wren ng thornton
-- License     :  BSD3
-- Maintainer  :  wren@community.haskell.org
-- Stability   :  experimental
-- Portability :  portable
--
-- Some basic monad combinators that "Control.Monad" lacks. Many
-- of these functions have been adopted by the @IfElse@ package:
-- <http://hackage.haskell.org/cgi-bin/hackage-scripts/package/IfElse>
----------------------------------------------------------------
module Control.Monad.Extras
    (
    -- * Monadic conditionals
      ifM, whenM, unlessM
      
    -- * Looping constructs
    , whileM, untilM
    
    -- * Specialized @return@s
    , return', returning
    
    -- * Other functions
    , liftM', maybeMP, maybeEither
    ) where
    
import Control.Monad
----------------------------------------------------------------


-- | Choose a computation to run based on the boolean.
ifM :: (Monad m) => m Bool -> m a -> m a -> m a
{-# SPECIALIZE ifM :: IO Bool -> IO () -> IO () -> IO () #-}
ifM mb mt mf = mb >>= \b -> if b then mt else mf
{-# INLINE ifM #-}


-- | If the boolean is true, then run the computation.
whenM :: (Monad m) => m Bool -> m () -> m ()
{-# SPECIALIZE whenM :: IO Bool -> IO () -> IO () #-}
whenM mb mt = mb >>= \b -> when b mt
{-# INLINE whenM #-}


-- | If the boolean is false, then run the computation.
unlessM :: (Monad m) => m Bool -> m () -> m ()
{-# SPECIALIZE unlessM :: IO Bool -> IO () -> IO () #-}
unlessM mb mf = mb >>= \b -> unless b mf
{-# INLINE unlessM #-}


-- | Execute a monadic action so long as a monadic boolean returns
-- true.
whileM :: (Monad m) => m Bool -> m () -> m ()
{-# SPECIALIZE whileM :: IO Bool -> IO () -> IO () #-}
whileM mb m = do b <- mb ; when b (m >> whileM mb m)


-- Cf. Prelude.until :: (a -> Bool) -> (a -> a) -> a -> a
-- | Negation of 'whileM': execute an action so long as the boolean
-- returns false.
untilM :: (Monad m) => m Bool -> m () -> m ()
{-# SPECIALIZE untilM :: IO Bool -> IO () -> IO () #-}
untilM mb m = do b <- mb ; unless b (m >> untilM mb m)


-- | Strict version of 'return' because usually we don't need that
-- extra thunk.
return' :: (Monad m) => a -> m a
return' x = return $! x
{-# INLINE return' #-}


-- | Take an action and make it into a side-effecting 'return'.
-- Because I seem to keep running into @m ()@ and the like.
-- 
-- This is the moral inverse of @Control.Monad.void :: Functor f
-- => f a -> f ()@ which is added in @base-4.3.0.0@.
returning :: (Monad m) => (a -> m b) -> (a -> m a)
f `returning` x = f x >> return x
{-# INLINE returning #-}
infixr 8 `returning`


-- For reference this is also helpful:
-- >    liftM2 (>>) f g == \x -> f x >> g x


-- | This conversion is common enough to make a name for.
maybeMP :: (MonadPlus m) => Maybe a -> m a
{-# SPECIALIZE maybeMP :: Maybe a -> Maybe a #-}
maybeMP Nothing  = mzero
maybeMP (Just x) = return x
{-# INLINE maybeMP #-}


-- | Strict version of 'liftM'. Useful, for example, to make 'foldM'
-- strict which can be necessary to avoid stack overflows.
liftM' :: (Monad m) => (a -> b) -> m a -> m b
liftM' f mx = mx >>= (return $!) . f
{-# INLINE liftM' #-}


-- | A variant of 'maybe' (or 'either') for converting the Maybe
-- monad into the (Either a) monad, i.e. for naming your errors.
maybeEither :: a -> Maybe b -> Either a b
maybeEither x Nothing  = Left x
maybeEither _ (Just y) = Right y
{-# INLINE maybeEither #-}

----------------------------------------------------------------
----------------------------------------------------------- fin.
