module Control.Monad.Loop.Internal
( LoopT(..), Loop, loop
, Unroll(..), UnTL, Unrolling(), noUnroll
, cons, continue, continue_, break_, exec_
, iterate, forever, for, unfoldl, while
) where
import Control.Applicative (Applicative(..), (<$>), liftA2)
import Control.Category ((<<<), (>>>))
import Control.Monad (unless)
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import qualified GHC.TypeLits as TL
import Data.Foldable
import Data.Functor.Identity
import Data.Maybe (fromJust, isJust)
import Data.Traversable (Traversable(..))
import Prelude hiding (foldr, iterate)
newtype LoopT m a = LoopT
{ runLoopT :: forall r. (a -> m r -> m r -> m r) -> m r -> m r -> m r }
type Loop = LoopT Identity
loop :: Loop a -> Loop a
loop = id
instance Functor (LoopT m) where
fmap f xs = LoopT $ \yield -> runLoopT xs (yield . f)
instance Applicative (LoopT m) where
pure a = LoopT $ \yield -> yield a
fs <*> as = LoopT $ \yield next ->
runLoopT fs (\f next' _ -> runLoopT (fmap f as) yield next' next) next
instance Monad (LoopT m) where
return = pure
as >>= f = LoopT $ \yield next ->
runLoopT as (\a next' _ -> runLoopT (f a) yield next' next) next
instance MonadTrans LoopT where
lift m = LoopT $ \yield next brk -> m >>= \a -> yield a next brk
instance MonadIO m => MonadIO (LoopT m) where
liftIO = lift . liftIO
instance (Applicative m, Foldable m) => Foldable (LoopT m) where
foldr f r xs = foldr (<<<) id inner r
where
yield a next _ = (f a <<<) <$> next
inner = runLoopT xs yield (pure id) (pure id)
foldl' f r xs = foldl' (!>>>) id inner r
where
(!>>>) h g = h >>> (g $!)
yield a next _ = (flip f a >>>) <$> next
inner = runLoopT xs yield (pure id) (pure id)
instance (Applicative m, Foldable m) => Traversable (LoopT m) where
sequenceA = foldr (liftA2 cons) (pure continue_)
cons :: a -> LoopT m a -> LoopT m a
cons a as = LoopT $ \yield next brk -> yield a (runLoopT as yield next brk) next
continue :: a -> LoopT m a
continue a = LoopT $ \yield next -> yield a next
continue_ :: LoopT m a
continue_ = LoopT $ \_ next _ -> next
break_ :: LoopT m a
break_ = LoopT $ \_ _ brk -> brk
exec_ :: Applicative m => LoopT m a -> m ()
exec_ xs = runLoopT xs (\_ next _ -> next) (pure ()) (pure ())
iterate
:: Unrolling (UnTL n)
=> Unroll n
-> a
-> (a -> a)
-> LoopT m a
iterate unroll = \a0 adv -> LoopT $ \yield next _ ->
let go a = unrollIterate (fromTypeLit unroll) a adv yield go next
in go a0
forever :: Unrolling (UnTL n) => Unroll n -> LoopT m ()
forever unroll = iterate unroll () id
for
:: Unrolling (UnTL n)
=> Unroll n
-> a
-> (a -> Bool)
-> (a -> a)
-> LoopT m a
for unroll = \a0 cond adv -> LoopT $ \yield next _ ->
let go a = unrollFor (fromTypeLit unroll) a cond adv yield go next
in if cond a0 then go a0 else next
unfoldl
:: Unrolling (UnTL n)
=> Unroll n
-> (i -> Maybe (i, a))
-> i
-> LoopT m a
unfoldl unroll = \unf i0 ->
fromJust . fmap snd <$> for unroll (unf i0) isJust (>>= unf . fst)
while
:: (Unrolling (UnTL n), Monad m)
=> Unroll n
-> m Bool
-> LoopT m ()
while unroll = \cond -> do
forever unroll
p <- lift cond
unless p break_
data Unroll (n :: TL.Nat) = Unroll
data Nat = S !Nat | Z
data UnrollInd (n :: Nat) = UnrollInd
noUnroll :: Unroll 1
noUnroll = Unroll
predUnroll :: UnrollInd (S n) -> UnrollInd n
predUnroll UnrollInd = UnrollInd
type family UnTL (n :: TL.Nat) :: Nat where
UnTL 1 = S Z
UnTL n = S (UnTL ((TL.-) n 1))
fromTypeLit :: Unroll n -> UnrollInd (UnTL n)
fromTypeLit Unroll = UnrollInd
class Unrolling (n :: Nat) where
unrollFor
:: UnrollInd n
-> a -> (a -> Bool) -> (a -> a)
-> (a -> m r -> m r -> m r) -> (a -> m r) -> m r -> m r
unrollIterate
:: UnrollInd n
-> a -> (a -> a)
-> (a -> m r -> m r -> m r) -> (a -> m r) -> m r -> m r
instance Unrolling Z where
unrollFor UnrollInd a _ _ _ next _ = next a
unrollIterate UnrollInd a _ _ next _ = next a
instance Unrolling n => Unrolling (S n) where
unrollFor unroll a cond adv yield next brk =
yield a descend brk
where
a' = adv a
descend | cond a' = unrollFor (predUnroll unroll) a' cond adv yield next brk
| otherwise = brk
unrollIterate unroll a adv yield next brk =
yield a descend brk
where
descend = unrollIterate (predUnroll unroll) (adv a) adv yield next brk