{-# LANGUAGE Arrows #-}
{-# LANGUAGE CPP    #-}
-- |
-- Copyright  : (c) Ivan Perez and Manuel Baerenz, 2016
-- License    : BSD3
-- Maintainer : ivan.perez@keera.co.uk
--
-- Useful auxiliary functions and definitions.
module Data.MonadicStreamFunction.Util where

-- External imports
import Control.Arrow    (arr, returnA, (&&&), (<<<), (>>>))
import Control.Category (id, (.))
import Control.Monad    (when)
import Data.VectorSpace (VectorSpace, zeroVector, (^+^))
import Prelude          hiding (id, (.))

#if !MIN_VERSION_base(4,8,0)
import Data.Monoid (Monoid, mempty, mappend)
#endif

-- Internal imports
import Data.MonadicStreamFunction.Core                  (MSF, arrM, feedback)
import Data.MonadicStreamFunction.Instances.ArrowChoice ()

-- * Streams and sinks

-- | A stream is an 'MSF' that produces outputs, while ignoring the input. It
-- can obtain the values from a monadic context.
type MStream m a = MSF m () a

-- | A sink is an 'MSF' that consumes inputs, while producing no output. It
-- can consume the values with side effects.
type MSink m a = MSF m a ()

-- * Analogues of 'map' and 'fmap'

-- | Apply an 'MSF' to every input. Freezes temporarily if the input is
-- 'Nothing', and continues as soon as a 'Just' is received.
mapMaybeS :: Monad m => MSF m a b -> MSF m (Maybe a) (Maybe b)
mapMaybeS :: forall (m :: * -> *) a b.
Monad m =>
MSF m a b -> MSF m (Maybe a) (Maybe b)
mapMaybeS MSF m a b
msf = proc Maybe a
maybeA -> case Maybe a
maybeA of
  Just a
a  -> forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr forall a. a -> Maybe a
Just forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
<<< MSF m a b
msf -< a
a
  Maybe a
Nothing -> forall (a :: * -> * -> *) b. Arrow a => a b b
returnA          -< forall a. Maybe a
Nothing

-- * Adding side effects

-- | Applies a function to produce an additional side effect and passes the
-- input unchanged.
withSideEffect :: Monad m => (a -> m b) -> MSF m a a
withSideEffect :: forall (m :: * -> *) a b. Monad m => (a -> m b) -> MSF m a a
withSideEffect a -> m b
method = (forall {k} (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& forall (m :: * -> *) a b. Monad m => (a -> m b) -> MSF m a b
arrM a -> m b
method) forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr forall a b. (a, b) -> a
fst

-- | Produces an additional side effect and passes the input unchanged.
withSideEffect_ :: Monad m => m b -> MSF m a a
withSideEffect_ :: forall (m :: * -> *) b a. Monad m => m b -> MSF m a a
withSideEffect_ m b
method = forall (m :: * -> *) a b. Monad m => (a -> m b) -> MSF m a a
withSideEffect forall a b. (a -> b) -> a -> b
$ forall a b. a -> b -> a
const m b
method

-- * Delays

-- | Delay a signal by one sample.
iPre :: Monad m
     => a         -- ^ First output
     -> MSF m a a
iPre :: forall (m :: * -> *) a. Monad m => a -> MSF m a a
iPre a
firsta = forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback a
firsta forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr forall {b} {a}. (b, a) -> (a, b)
swap
  where
    swap :: (b, a) -> (a, b)
swap (b
a, a
b) = (a
b, b
a)

-- | Preprends a fixed output to an 'MSF'. The first input is completely
-- ignored.
iPost :: Monad m => b -> MSF m a b -> MSF m a b
iPost :: forall (m :: * -> *) b a. Monad m => b -> MSF m a b -> MSF m a b
iPost b
b MSF m a b
sf = MSF m a b
sf forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback (forall a. a -> Maybe a
Just b
b) forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr forall a b. (a -> b) -> a -> b
$ \(b
c, Maybe b
ac) -> case Maybe b
ac of
  Maybe b
Nothing -> (b
c, forall a. Maybe a
Nothing)
  Just b
b' -> (b
b', forall a. Maybe a
Nothing))

-- | Preprends a fixed output to an 'MSF', shifting the output.
next :: Monad m => b -> MSF m a b -> MSF m a b
next :: forall (m :: * -> *) b a. Monad m => b -> MSF m a b -> MSF m a b
next b
b MSF m a b
sf = MSF m a b
sf forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall (m :: * -> *) a. Monad m => a -> MSF m a a
iPre b
b

-- | Buffers and returns the elements in FIFO order, returning 'Nothing'
-- whenever the buffer is empty.
fifo :: Monad m => MSF m [a] (Maybe a)
fifo :: forall (m :: * -> *) a. Monad m => MSF m [a] (Maybe a)
fifo = forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback [] (forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr (forall x. [x] -> (Maybe x, [x])
safeSnoc forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry forall x. [x] -> [x] -> [x]
fifoAppend))
  where
    -- | Append a new list to an accumulator in FIFO order.
    fifoAppend :: [x] -> [x] -> [x]
    fifoAppend :: forall x. [x] -> [x] -> [x]
fifoAppend [x]
as [x]
accum = [x]
accum forall x. [x] -> [x] -> [x]
++ [x]
as

    -- | Split a list into the head and the tail.
    safeSnoc :: [x] -> (Maybe x, [x])
    safeSnoc :: forall x. [x] -> (Maybe x, [x])
safeSnoc []     = (forall a. Maybe a
Nothing, [])
    safeSnoc (x
x:[x]
xs) = (forall a. a -> Maybe a
Just x
x, [x]
xs)

-- * Folding

-- ** Folding for 'VectorSpace' instances

-- | Count the number of simulation steps. Produces 1, 2, 3,...
count :: (Num n, Monad m) => MSF m a n
count :: forall n (m :: * -> *) a. (Num n, Monad m) => MSF m a n
count = forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr (forall a b. a -> b -> a
const n
1) forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall (m :: * -> *) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith forall a. Num a => a -> a -> a
(+) n
0

-- | Sums the inputs, starting from zero.
sumS :: (VectorSpace v s, Monad m) => MSF m v v
sumS :: forall v s (m :: * -> *). (VectorSpace v s, Monad m) => MSF m v v
sumS = forall v s (m :: * -> *).
(VectorSpace v s, Monad m) =>
v -> MSF m v v
sumFrom forall v a. VectorSpace v a => v
zeroVector

-- | Sums the inputs, starting from an initial vector.
sumFrom :: (VectorSpace v s, Monad m) => v -> MSF m v v
sumFrom :: forall v s (m :: * -> *).
(VectorSpace v s, Monad m) =>
v -> MSF m v v
sumFrom = forall (m :: * -> *) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith forall v a. VectorSpace v a => v -> v -> v
(^+^)

-- ** Folding for monoids

-- | Accumulate the inputs, starting from 'mempty'.
mappendS :: (Monoid n, Monad m) => MSF m n n
mappendS :: forall n (m :: * -> *). (Monoid n, Monad m) => MSF m n n
mappendS = forall n (m :: * -> *). (Monoid n, Monad m) => n -> MSF m n n
mappendFrom forall a. Monoid a => a
mempty
{-# INLINE mappendS #-}

-- | Accumulate the inputs, starting from an initial monoid value.
mappendFrom :: (Monoid n, Monad m) => n -> MSF m n n
mappendFrom :: forall n (m :: * -> *). (Monoid n, Monad m) => n -> MSF m n n
mappendFrom = forall (m :: * -> *) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith forall a. Monoid a => a -> a -> a
mappend

-- ** Generic folding \/ accumulation

-- | Applies a function to the input and an accumulator, outputting the updated
-- accumulator. Equal to @\f s0 -> feedback s0 $ arr (uncurry f >>> dup)@.
accumulateWith :: Monad m => (a -> s -> s) -> s -> MSF m a s
accumulateWith :: forall (m :: * -> *) a s.
Monad m =>
(a -> s -> s) -> s -> MSF m a s
accumulateWith a -> s -> s
f s
s0 = forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback s
s0 forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr (a, s) -> (s, s)
g
  where
    g :: (a, s) -> (s, s)
g (a
a, s
s) = let s' :: s
s' = a -> s -> s
f a
a s
s in (s
s', s
s')

-- | Applies a transfer function to the input and an accumulator, returning the
-- updated accumulator and output.
mealy :: Monad m => (a -> s -> (b, s)) -> s -> MSF m a b
mealy :: forall (m :: * -> *) a s b.
Monad m =>
(a -> s -> (b, s)) -> s -> MSF m a b
mealy a -> s -> (b, s)
f s
s0 = forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback s
s0 forall a b. (a -> b) -> a -> b
$ forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry a -> s -> (b, s)
f

-- * Unfolding

-- | Generate outputs using a step-wise generation function and an initial
-- value.
unfold :: Monad m => (a -> (b, a)) -> a -> MSF m () b
unfold :: forall (m :: * -> *) a b.
Monad m =>
(a -> (b, a)) -> a -> MSF m () b
unfold a -> (b, a)
f a
a = forall (m :: * -> *) c a b.
Monad m =>
c -> MSF m (a, c) (b, c) -> MSF m a b
feedback a
a (forall (a :: * -> * -> *) b c. Arrow a => (b -> c) -> a b c
arr (forall a b. (a, b) -> b
snd forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> a -> (b, a)
f))

-- | Generate outputs using a step-wise generation function and an initial
-- value. Version of 'unfold' in which the output and the new accumulator are
-- the same. Should be equal to @\f a -> unfold (f >>> dup) a@.
repeatedly :: Monad m => (a -> a) -> a -> MSF m () a
repeatedly :: forall (m :: * -> *) a. Monad m => (a -> a) -> a -> MSF m () a
repeatedly a -> a
f = forall (m :: * -> *) a b.
Monad m =>
(a -> (b, a)) -> a -> MSF m () b
unfold forall a b. (a -> b) -> a -> b
$ a -> a
f forall {k} (cat :: k -> k -> *) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> forall {b}. b -> (b, b)
dup
  where
    dup :: b -> (b, b)
dup b
a = (b
a, b
a)

-- * Debugging

-- | Outputs every input sample, with a given message prefix.
trace :: Show a => String -> MSF IO a a
trace :: forall a. Show a => String -> MSF IO a a
trace = forall (m :: * -> *) a.
(Monad m, Show a) =>
(String -> m ()) -> String -> MSF m a a
traceWith String -> IO ()
putStrLn

-- | Outputs every input sample, with a given message prefix, using an
-- auxiliary printing function.
traceWith :: (Monad m, Show a) => (String -> m ()) -> String -> MSF m a a
traceWith :: forall (m :: * -> *) a.
(Monad m, Show a) =>
(String -> m ()) -> String -> MSF m a a
traceWith String -> m ()
method String
msg =
  forall (m :: * -> *) a b. Monad m => (a -> m b) -> MSF m a a
withSideEffect (String -> m ()
method forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (String
msg forall x. [x] -> [x] -> [x]
++) forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. forall a. Show a => a -> String
show)

-- | Outputs every input sample, with a given message prefix, using an
-- auxiliary printing function, when a condition is met.
traceWhen :: (Monad m, Show a)
          => (a -> Bool)
          -> (String -> m ())
          -> String
          -> MSF m a a
traceWhen :: forall (m :: * -> *) a.
(Monad m, Show a) =>
(a -> Bool) -> (String -> m ()) -> String -> MSF m a a
traceWhen a -> Bool
cond String -> m ()
method String
msg = forall (m :: * -> *) a b. Monad m => (a -> m b) -> MSF m a a
withSideEffect forall a b. (a -> b) -> a -> b
$ \a
a ->
  forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (a -> Bool
cond a
a) forall a b. (a -> b) -> a -> b
$ String -> m ()
method forall a b. (a -> b) -> a -> b
$ String
msg forall x. [x] -> [x] -> [x]
++ forall a. Show a => a -> String
show a
a

-- | Outputs every input sample, with a given message prefix, when a condition
-- is met, and waits for some input \/ enter to continue.
pauseOn :: Show a => (a -> Bool) -> String -> MSF IO a a
pauseOn :: forall a. Show a => (a -> Bool) -> String -> MSF IO a a
pauseOn a -> Bool
cond = forall (m :: * -> *) a.
(Monad m, Show a) =>
(a -> Bool) -> (String -> m ()) -> String -> MSF m a a
traceWhen a -> Bool
cond forall a b. (a -> b) -> a -> b
$ \String
s -> forall a. Show a => a -> IO ()
print String
s forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> IO String
getLine forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall (m :: * -> *) a. Monad m => a -> m a
return ()