{-# LANGUAGE LambdaCase #-}

-- |
-- Module:     Drama.Loop
-- Stability:  experimental
-- License:    BSD-3-Clause
-- Copyright:  © 2021 Evan Relf
-- Maintainer: evan@evanrelf.com

module Drama.Loop
  ( loop
  , continue
  , stop

    -- * Re-exports
  , forever
  )
where

import Control.Monad (forever)


-- | Loop indefinitely with state. Use `Control.Monad.forever` for stateless
-- infinite loops.
--
-- ===== __ Example __
--
-- > counter :: Process NoMsg ()
-- > counter = loop (10 :: Int) \count -> do
-- >   liftIO $ print count
-- >   if count > 0
-- >     then continue (count - 1)
-- >     else exit ()
--
-- @since 0.3.0.0
loop
  :: Monad m
  => s
  -- ^ Initial state
  -> (s -> m (Either s a))
  -- ^ Action to perform, returning either a new state to continue looping, or
  -- a final value to stop looping.
  -> m a
loop :: s -> (s -> m (Either s a)) -> m a
loop s
s0 s -> m (Either s a)
k =
  s -> m (Either s a)
k s
s0 m (Either s a) -> (Either s a -> m a) -> m a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Left s
s -> s -> (s -> m (Either s a)) -> m a
forall (m :: * -> *) s a.
Monad m =>
s -> (s -> m (Either s a)) -> m a
loop s
s s -> m (Either s a)
k
    Right a
x -> a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
x


-- | Continue looping with some new state.
--
-- prop> continue s = pure (Left s)
--
-- @since 0.3.0.0
continue :: Monad m => s -> m (Either s a)
continue :: s -> m (Either s a)
continue s
s = Either s a -> m (Either s a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (s -> Either s a
forall a b. a -> Either a b
Left s
s)


-- | Stop looping and return with a final value.
--
-- prop> exit x = pure (Right x)
--
-- @since 0.3.0.0
stop :: Monad m => a -> m (Either s a)
stop :: a -> m (Either s a)
stop a
x = Either s a -> m (Either s a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> Either s a
forall a b. b -> Either a b
Right a
x)