module HaskellWorks.Control.Monad
  ( whileM,
    unlessM,

    whileNothingM,

    repeatNUntilM_,
    repeatNWhileM_,
  ) where

import           HaskellWorks.Prelude

whileM :: Monad m => m Bool -> m ()
whileM :: forall (m :: * -> *). Monad m => m Bool -> m ()
whileM m Bool
act = do
  Bool
b <- m Bool
act
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
b (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ m Bool -> m ()
forall (m :: * -> *). Monad m => m Bool -> m ()
whileM m Bool
act

unlessM :: Monad m => m Bool -> m ()
unlessM :: forall (m :: * -> *). Monad m => m Bool -> m ()
unlessM m Bool
act = do
  Bool
b <- m Bool
act
  Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
b (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ m Bool -> m ()
forall (m :: * -> *). Monad m => m Bool -> m ()
unlessM m Bool
act

whileNothingM :: Monad m => m (Maybe a) -> m a
whileNothingM :: forall (m :: * -> *) a. Monad m => m (Maybe a) -> m a
whileNothingM m (Maybe a)
act =
  m (Maybe a)
act m (Maybe a) -> (Maybe a -> m a) -> m a
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= m a -> (a -> m a) -> Maybe a -> m a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (m (Maybe a) -> m a
forall (m :: * -> *) a. Monad m => m (Maybe a) -> m a
whileNothingM m (Maybe a)
act) a -> m a
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

-- | Repeat an action n times until the action returns True.
repeatNUntilM_ :: ()
  => Monad m
  => Int
  -> (Int -> m Bool)
  -> m ()
repeatNUntilM_ :: forall (m :: * -> *). Monad m => Int -> (Int -> m Bool) -> m ()
repeatNUntilM_ Int
n Int -> m Bool
action = Int -> m ()
go Int
0
  where
    go :: Int -> m ()
go Int
i =
      Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
n) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
        Bool
shouldTerminate <- Int -> m Bool
action Int
i
        Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
shouldTerminate (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> m ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

-- | Repeat an action n times while the action returns True.
repeatNWhileM_ :: ()
  => Monad m
  => Int
  -> (Int -> m Bool)
  -> m ()
repeatNWhileM_ :: forall (m :: * -> *). Monad m => Int -> (Int -> m Bool) -> m ()
repeatNWhileM_ Int
n Int -> m Bool
action = Int -> m ()
go Int
0
  where
    go :: Int -> m ()
go Int
i =
      Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
n) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
        Bool
shouldContinue <- Int -> m Bool
action Int
i
        Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
shouldContinue (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> m ()
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)