As a high-level consideration, I would say that your type is a state-transformed stream transformer. Something a little confusing here is that your type is defined as
newtype R a = R (a , a -> R a)
instead
newtype R a = R (a -> (R a, a))
which would be a little more natural in a streaming context, because you usually donโt produce anything if you havenโt received anything yet. Then your functions will also have simpler types:
alpha, omage :: R String cascade :: R a -> [a] -> [a]
If we try to generalize this idea of โโa flow transformer, we will soon realize that the case when we convert list a to list a is just a special case. With the right infrastructure, we could also make a list of b s. Therefore, we are trying to generalize the type of R :
newtype R ab = R (a -> (R ab, b))
I saw a structure called Circuit , which turns out to be a full-blown arrow . Arrows are a generalization of the concept of functions and are an even more powerful construction than monads. I can't pretend to understand theoretical theory, but it's definitely fun to play with them. For example, the trivial conversion is simply Cat.id :
import Control.Category import Control.Arrow import Prelude hiding ((.), id) import qualified Data.List as L -- ... Definition of Circuit and instances cascade :: Circuit ab -> [a] -> [b] cascade cir = snd . L.mapAccumL unCircuit cir -- ghci> cascade (Cat.id) [1,2,3,4] [1,2,3,4]
We can also model the state by parameterizing the circuit, which we return as a continuation:
countingCircuit :: (a -> b) -> Circuit a (Int, b) countingCircuit f = cir 0 where cir i = Circuit $ \x -> (cir (i+1), (i, fx)) -- ghci> cascade (countingCircuit (+5)) [10,3,2,11] [(0,15),(1,8),(2,7),(3,16)]
And the fact that our schema type is a category gives us a good way to create schemas:
ghci> cascade (countingCircuit (+5) . arr (*2)) [10,3,2,11] [(0,25),(1,11),(2,9),(3,27)]