\begin{comment}
\begin{code}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
module LiveCoding.Cell.Feedback where
import Control.Arrow
import Data.Data
import Data.Maybe (fromMaybe)
import LiveCoding.Cell
\end{code}
\end{comment}
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:
\begin{code}
feedback
:: (Monad m, Data s)
=> s
-> Cell m (a, s) (b, s)
-> Cell m a b
\end{code}
Let us have a look at its internal state:
\begin{code}
data Feedback sPrevious sAdditional = Feedback
{ sPrevious :: sPrevious
, sAdditional :: sAdditional
} deriving Data
\end{code}
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?}
\begin{comment}
\begin{code}
feedback sAdditional (Cell sPrevious step) = Cell { .. }
where
cellState = Feedback { .. }
cellStep Feedback { .. } a = do
((!b, !sAdditional'), sPrevious') <- step sPrevious (a, sAdditional)
return (b, Feedback sPrevious' sAdditional')
feedback cellState (ArrM f) = Cell { .. }
where
cellStep state a = f (a, state)
\end{code}
\end{comment}
It enables us to write delays:
\begin{code}
delay :: (Data s, Monad m) => s -> Cell m s s
delay s = feedback s $ arr swap
where
swap (sNew, sOld) = (sOld, sNew)
\end{code}
\mintinline{haskell}{feedback} can be used for accumulation of data.
For example, \mintinline{haskell}{sumC} now becomes:
\begin{code}
sumFeedback
:: (Monad m, Num a, Data a)
=> Cell m a a
sumFeedback = feedback 0 $ arr
$ \(a, accum) -> (accum, a + accum)
\end{code}