{-# LANGUAGE Arrows #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}

module LiveCoding.Cell.Feedback where

-- base
import Control.Arrow
import Data.Data
import Data.Maybe (fromMaybe)

-- essence-of-live-coding
import LiveCoding.Cell

We would like to have all basic primitives needed to develop standard synchronous signal processing components,
without touching the \mintinline{haskell}{Cell} constructor anymore.
One crucial bit is missing to achieve this goal:
Encapsulating state.
The most general such construction is the feedback loop:
  :: (Monad m, Data s)
  =>                s
  -> Cell   m   (a, s) (b, s)
  -> Cell   m    a      b
Let us have a look at its internal state:
data Feedback sPrevious sAdditional = Feedback
  { sPrevious   :: sPrevious
  , sAdditional :: sAdditional
In \mintinline{haskell}{feedback sAdditional cell},
the \mintinline{haskell}{cell} has state \mintinline{haskell}{sPrevious},
and to this state we add \mintinline{haskell}{sAdditional}.
The additional state is received by \mintinline{haskell}{cell} as explicit input,
and \mintinline{haskell}{feedback} hides it.

Note that \mintinline{haskell}{feedback} and \mintinline{haskell}{loop} are different.
While \mintinline{haskell}{loop} provides immediate recursion, it doesn't add new state.
\mintinline{haskell}{feedback} requires an initial state and delays it,
but in turn it is always safe to use since it does not use \mintinline{haskell}{mfix}.

\fxwarning{Possibly remark on Data instance of s?}
newtype Feedback s s' = Feedback (s, s')
  deriving Data

feedback s (Cell state step) = Cell { .. }
    cellState = Feedback (state, s)
    cellStep (Feedback (state, s)) a = do
      ((b, s'), state') <- step state (a, s)
      return (b, Feedback (state', s'))
feedback cellState (ArrM f) = Cell { .. }
    cellStep state a = f (a, state)
It enables us to write delays:
delay :: (Data s, Monad m) => s -> Cell m s s
delay s = feedback s $ arr swap
    swap (sNew, sOld) = (sOld, sNew)
\mintinline{haskell}{feedback} can be used for accumulation of data.
For example, \mintinline{haskell}{sumC} now becomes:
  :: (Monad m, Num a, Data a)
  => Cell m a a
sumFeedback = feedback 0 $ arr
  $ \(a, accum) -> (accum, a + accum)

\fxerror{Mention keepJust and keep}
  :: (Monad m, Data a)
  => Cell m (Maybe a) (Maybe a)
keepJust = feedback Nothing $ arr keep
    keep (Nothing, Nothing) = (Nothing, Nothing)
    keep (_, Just a) = (Just a, Just a)
    keep (Just a, Nothing) = (Just a, Just a)

-- | Initialise with a value 'a'.
--   If the input is 'Nothing', @keep a@ will output the stored indefinitely.
--   A new value can be stored by inputting 'Maybe a'.
keep :: (Data a, Monad m) => a -> Cell m (Maybe a) a
keep a = feedback a $ proc (ma, aOld) -> do
  let aNew = fromMaybe aOld ma
  returnA -< (aNew, aNew)