-- | This module contains operations on monadic streams that are asynchronous,
--   i.e. that change the speed at which data enters or leaves the 'MSF'.

module Data.MonadicStreamFunction.Async where

-- Internal
import Data.MonadicStreamFunction.Core
import Data.MonadicStreamFunction.InternalCore
import Data.MonadicStreamFunction.Util (MStream)

{- |
Concatenates a monadic stream of lists to a monadic stream.
The stream of lists will be called exactly when new data is needed.

Example:

>>> let intstream = constS $ putStrLn "Enter a list of Ints:" >> readLn :: MStream IO [Int]
>>> reactimate $ concatS intstream >>> arrM print
Enter a list of Ints:
[1,2,33]
1
2
33
Enter a list of Ints:
[]
Enter a list of Ints:
[]
Enter a list of Ints:
[1,2]
1
2
Enter a list of Ints:
...

Beware that @concatS msf@ becomes unproductive when @msf@ starts outputting empty lists forever.
This is ok:

>>> let boolToList b = if b then ["Yes"] else []
>>> let everyOddEmpty = count >>> arr (even >>> boolToList)
>>> reactimate $ concatS everyOddEmpty >>> arrM print
"Yes"
"Yes"
"Yes"
"Yes"
"Yes"
...

But this will be caught in a loop:

>>> let after3Empty = count >>> arr ((<= 3) >>> boolToList)
>>> reactimate $ concatS after3Empty  >>> arrM print
"Yes"
"Yes"
"Yes"
^CInterrupted.
-}
concatS :: Monad m => MStream m [b] -> MStream m b
concatS :: MStream m [b] -> MStream m b
concatS MStream m [b]
msf = (() -> m (b, MStream m b)) -> MStream m b
forall (m :: * -> *) a b. (a -> m (b, MSF m a b)) -> MSF m a b
MSF ((() -> m (b, MStream m b)) -> MStream m b)
-> (() -> m (b, MStream m b)) -> MStream m b
forall a b. (a -> b) -> a -> b
$ \()
_ -> MStream m [b] -> [b] -> m (b, MStream m b)
forall (m :: * -> *) a a.
Monad m =>
MSF m () [a] -> [a] -> m (a, MSF m a a)
tick MStream m [b]
msf []
  where
    tick :: MSF m () [a] -> [a] -> m (a, MSF m a a)
tick MSF m () [a]
msf' (a
b:[a]
bs) = (a, MSF m a a) -> m (a, MSF m a a)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
b, (a -> m (a, MSF m a a)) -> MSF m a a
forall (m :: * -> *) a b. (a -> m (b, MSF m a b)) -> MSF m a b
MSF ((a -> m (a, MSF m a a)) -> MSF m a a)
-> (a -> m (a, MSF m a a)) -> MSF m a a
forall a b. (a -> b) -> a -> b
$ \a
_ -> MSF m () [a] -> [a] -> m (a, MSF m a a)
tick MSF m () [a]
msf' [a]
bs)
    tick MSF m () [a]
msf' []     = do
      ([a]
bs, MSF m () [a]
msf'') <- MSF m () [a] -> () -> m ([a], MSF m () [a])
forall (m :: * -> *) a b. MSF m a b -> a -> m (b, MSF m a b)
unMSF MSF m () [a]
msf' ()
      MSF m () [a] -> [a] -> m (a, MSF m a a)
tick MSF m () [a]
msf'' [a]
bs
-- TODO Maybe this can be written more nicely with exceptions?
-- Similarly takeS :: Int -> MSF m a b -> MSFExcept m a b () throws an exception after n ticks
-- Or with merge?