module Control.Monad.HT where

import Control.Monad (liftM, liftM2, )
import Prelude hiding (repeat, until, )


infixr 1 <=<

{- |
Also present in newer versions of the 'base' package.
-}
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
(<=<) f g = (f =<<) . g


{- |
Monadic 'List.repeat'.
-}
repeat :: (Monad m) => m a -> m [a]
repeat x =
   let go = liftM2 (:) x go in go

{-# DEPRECATED untilM "use M.until" #-}
{- | repeat action until result fulfills condition -}
until, untilM :: (Monad m) => (a -> Bool) -> m a -> m a
untilM = until
until p m =
   let go =
         do x <- m
            if p x
              then return x
              else go
   in  go

{-# DEPRECATED iterateLimitM "use M.iterateLimit" #-}
{- | parameter order equal to that of 'nest' -}
iterateLimit, iterateLimitM :: Monad m => Int -> (a -> m a) -> a -> m [a]
iterateLimitM = iterateLimit
iterateLimit m f =
   let aux n x =
          liftM (x:) $
          if n==0
            then return []
            else aux (n-1) =<< f x
   in  aux m

{- |
Lazy monadic conjunction.
That is, when the first action returns @False@,
then @False@ is immediately returned, without running the second action.
-}
andLazy :: (Monad m) => m Bool -> m Bool -> m Bool
andLazy m0 m1 =
   m0 >>= \b ->
   if b
     then m1
     else return False

{- |
Lazy monadic disjunction.
That is, when the first action returns @True@,
then @True@ is immediately returned, without running the second action.
-}
orLazy :: (Monad m) => m Bool -> m Bool -> m Bool
orLazy m0 m1 =
   m0 >>= \b ->
   if b
     then return True
     else m1