{-# LANGUAGE BangPatterns #-}

-- | This is for trying out loop alternatives.
--
-- Names and types are subjects to change.
module Control.Loop.Internal
  ( loop
  , unsafeLoop
  ) where


-- | @loop start end f@: Loops from @start@ to @end@ (inclusive), executing @f@
-- on each iteration. Same as @forM_ [start..end] f@.
--
-- Uses `succ` inside, which does a bounds (overflow) check.
loop :: (Enum e, Eq e, Monad m) => e -> e -> (e -> m ()) -> m ()
loop :: e -> e -> (e -> m ()) -> m ()
loop e
start e
end e -> m ()
f = e -> m ()
go e
start
  where
    go :: e -> m ()
go !e
x | e
x e -> e -> Bool
forall a. Eq a => a -> a -> Bool
== e
end  = e -> m ()
f e
x
          | Bool
otherwise = e -> m ()
f e
x m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> e -> m ()
go (e -> e
forall a. Enum a => a -> a
succ e
x)

{-# INLINE loop #-}


-- | Like `loop`, but (sometimes) without bounds (overflow) check.
--
-- This circumvents the implementation of `succ` for the @Enum@ type
-- and uses @toEnum . (+ 1) . fromEnum@ instead, so it will break
-- on Enums that are not contiguous.
--
-- Note that some types (e.g. Word32) have bounds checks even for
-- `toEnum`.
unsafeLoop :: (Enum e, Eq e, Monad m) => e -> e -> (e -> m ()) -> m ()
unsafeLoop :: e -> e -> (e -> m ()) -> m ()
unsafeLoop e
start e
end e -> m ()
f = e -> m ()
go e
start
  where
    go :: e -> m ()
go !e
x | e
x e -> e -> Bool
forall a. Eq a => a -> a -> Bool
== e
end  = e -> m ()
f e
x
          | Bool
otherwise = e -> m ()
f e
x m () -> m () -> m ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> e -> m ()
go (e -> e
unsafeSucc e
x)
    unsafeSucc :: e -> e
unsafeSucc = Int -> e
forall a. Enum a => Int -> a
toEnum (Int -> e) -> (e -> Int) -> e -> e
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int -> Int) -> (e -> Int) -> e -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> Int
forall a. Enum a => a -> Int
fromEnum

{-# INLINE unsafeLoop #-}