Copyright | (c) Justin Le 2015 |
---|---|
License | MIT |
Maintainer | justin@jle.im |
Stability | unstable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module contains various Auto
transformers for manipulating the
flow of time/stepping rate of an Auto
.
Many of these are Auto
"transformers", meaning that they take in an
Auto
and return a transformed Auto
, with new stepping behavior.
For example, there is accelerate
:
accelerate
::Monad
m =>Int
->Auto
m a b ->Auto
m a [b]
turns an accelerate
nAuto
into an Auto
that "steps itself" n
times for every single input/step. The result is a list of the
results of each single step.
There are also various Auto
s for observing the passage of time
(count
) and actiong as a "delay" or a way to access the previously
stepped values of an Auto
.
- count :: (Serialize b, Num b) => Auto m a b
- count_ :: Num b => Auto m a b
- lastVal :: Serialize a => a -> Auto m a a
- lastVal_ :: a -> Auto m a a
- arrD :: Serialize b => (a -> b) -> b -> Auto m a b
- arrD_ :: Serialize b => (a -> b) -> b -> Auto m a b
- delay :: Serialize a => a -> Auto m a a
- delay_ :: a -> Auto m a a
- delayList :: (Serialize a, Monad m) => [a] -> Auto m a a
- delayList_ :: Monad m => [a] -> Auto m a a
- delayN :: (Serialize a, Monad m) => Int -> a -> Auto m a a
- delayN_ :: Monad m => Int -> a -> Auto m a a
- priming :: Monad m => [a] -> Auto m a b -> Auto m a b
- stretch :: (Serialize b, Monad m) => Int -> Auto m a b -> Auto m a b
- stretch_ :: Monad m => Int -> Auto m a b -> Auto m a b
- stretchB :: Monad m => Int -> Auto m a b -> Auto m a (Blip b)
- stretchAccumBy :: (Serialize a, Serialize b, Monad m) => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b
- stretchAccumBy_ :: Monad m => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b
- accelerate :: Monad m => Int -> Auto m a b -> Auto m a [b]
- accelerateWith :: Monad m => a -> Int -> Auto m a b -> Auto m a [b]
- accelOverList :: Monad m => Auto m a b -> Auto m [a] [b]
- skipTo :: Monad m => a -> Auto m a (b, Blip c) -> Auto m a ([b], c)
- fastForward :: Monad m => a -> Interval m a b -> Auto m a b
- fastForwardEither :: Monad m => a -> Auto m a (Either c b) -> Auto m a (b, [c])
A counter
count :: (Serialize b, Num b) => Auto m a b Source
A simple Auto
that ignores all input; its output stream counts
upwards from zero.
>>>
take 10 . streamAuto' count $ repeat ()
[0,1,2,3,4,5,6,7,8,9]
Manipulating time
Delaying
An Auto
that returns the last value received by it. Given an
"initial value" to output first.
From the signal processing world, this is known as the "lag operator" L.
This is (potentially) a very dangerous Auto
, because its usage and
its very existence opens the door to breaking denotative/declarative
style and devolving into imperative style coding. However, when used
where it is supposed to be used, it is more or less invaluable, and will
be an essential part of many programs.
Its main usage is for dealing with recursive bindings. If you ever are
laying out recursive bindings in a high-level/denotative way, you need
to have at least one value be able to have a "initial output" without
depending on anything else. lastVal
and delay
allow you to do this.
See the recursive
example for more information on the appropriate usage of lastVal
and
delay
.
>>>
streamAuto' (lastVal 100) [1..10]
[100,1,2,3,4,5,6,7,8,9]
Like arr
, but applies the function to the previous value of the
input, instead of the current value. Used for the same purposes as
lastVal
: to manage recursive bindings.
Warning: Don't use this to do imperative programming!
arrD id == lastVal
>>>
streamAuto' (arrD negate 100) [1..10]
[100,-1,-2,-3,-4,-5,-6,-7,-8,-9]
The non-resuming/non-serializing version of arrD
.
Like delay
, except has as many "initial values" as the input list.
Outputs every item in the input list in order before returning the first
received value.
delayList [y0] = delay y0
>>>
streamAuto' (delayList [3,5,7,11]) [1..10]
[3,5,7,11,1,2,3,4,5,6]
The non-resuming/non-serializing version of delayList
.
Like delay
, except delays the desired number of steps with the same
initial output value.
delayN n x0 = delayList (replicate n x0)
delayN 1 x0 = delay x0
>>>
streamAuto' (delayN 3 0) [1..10]
[0,0,0,1,2,3,4,5,6,7]
The non-resuming/non-serializing version of delayN
Priming
When first asked for output, "primes" the Auto
first by streaming it
with all of the given inputs first before processing the first input.
Aterwards, behaves like normal.
>>>
streamAuto' (priming [1,2,3] (sumFrom 0)) [1..10]
[7,9,12,16,21,27,34,42,51,61]
The Auto
behaves as if it had already "processed" the [1,2,3]
,
resulting in an accumulator of 6, before it starts taking in any input.
Normally this would be silly with an Auto'
, because the above is the
same as:
>>>
let (_, a) = overList' (sumFrom 0) [1,2,3]
>>>
streamAuto' a [1..10]
[7,9,12,16,21,27,34,42,51,61]
This becomes somewhat more useful when you have "monadic" Auto
s, and
want to defer the execution until during normal stepping:
>>>
_ <- streamAuto (priming [1,2,3] (arrM print)) [10,11,12]
1 -- IO effects 2 3 10 11 12
Stretching
"stretch" an Auto
out, slowing time.
will take one
input, repeat the same output stretch
n an
times (ignoring input), and then take
another. It ignores all inputs in between.
>>>
let a = stretch 2 (sumFrom 0)
>>>
streamAuto' a [1,8,5,4,3,7,2,0]
[1,1,6,6,9,9,11,11] -- [1,_,5,_,3,_,2 ,_ ] <-- the inputs
The non-resuming/non-serializing version of stretch
.
Like stretch
, but instead of holding the the "stretched" outputs,
outputs a blip stream that emits every time the stretched Auto
"progresses" (every n
ticks)
See stretch
for more information.
>>>
let a = stretchB 2 (accum (+) 0)
>>>
streamAuto' a [1,8,5,4,3,7,2,0]
[Blip 1, NoBlip, Blip 6, NoBlip, Blip 9, NoBlip, Blip 11, NoBlip]
stretchAccumBy :: (Serialize a, Serialize b, Monad m) => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b Source
A more general version of stretch
; instead of just ignoring and
dropping the "stretched/skipped intervals", accumulate all of them up
with the given accumulating function and then "step" them all at once on
every n
th tick. Also, stead of returning exactly the same output
every time over the stretched interval, output a function of the
original output during the stretched intervals.
>>>
streamAuto' (sumFrom 0) [1..10]
[1, 3, 6, 10, 15, 21, 28, 36, 45 ,55]>>>
streamAuto' (stretchAccumBy (+) negate 4 (sumFrom 0)) [1..10]
[1,-1,-1, -1, 15,-15,-15,-15, 45,-45]
Here, instead of feeding in a number every step, it "accumulates" all of
the inputs using +
and "blasts them into"
every 4 steps.
In between the blasts, it outputs the negated last seen result.sumFrom
0
You can recover the behavior of stretch
with
.stretchAccumBy
(flip const) id
stretchAccumBy_ :: Monad m => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b Source
The non-serialized/non-resuming version of stretchAccumBy
.
Accelerating
turns an accelerate
n aAuto
a
into an "accelerated" Auto
,
where every input is fed into the Auto
n
times. All of the results
are collected in the output.
The same input is fed repeatedly n
times.
>>>
streamAuto' (accelerate 3 (sumFrom 0)) [2,3,4]
[[2,4,6],[9,12,15],[19,23,27]] -- ^adding 2s ^adding 3s ^adding 4s
:: Monad m | |
=> a | default input value, during acceleration periods |
-> Int | acceleration factor |
-> Auto m a b |
|
-> Auto m a [b] |
is like accelerateWith
xd n a
, except instead
of feeding in the input accelerate
n an
times, it feeds the input in once and
repeats the "filler" xd
for the rest of the accelerating period.
>>>
streamAuto' (accelerateWith (-1) 3 (sumFrom 0)) [1,10,100]
[[1,0,-1],[9,8,7],[107,106,105]] -- ^ feed in 1 once and -1 twice -- ^ feed in 10 once and -1 twice -- ^ feed in 100 once and -1 twice
Accelerates the Auto
, so instead of taking an a
and returning
a b
, it takes a list of a
, "streams" the Auto
over each one, and
returns a list of b
results.
For example, if you normally feed
a 1, then a 2, then a 3,
you'd get a 1, then a 3, then a 6. But if you feed
sumFrom
0
a accelOverList
(sumFrom
0)[1,2]
, you'd get a [1,3]
, and if
you fed it a [3]
after, you'd get a [6]
.
Turns a [a] -> [b]
into an [[a]] -> [[b]]
; if you "chunk up" the
input stream a
s into chunks of input to feed all at once, the outputs
b
will be chunked up the same way.
>>>
streamAuto' (sumFrom 0) [1,2,3,4,5,6,7,8]
[1,3,6,10,15,21,28,36]>>>
streamAuto' (accelOverList (sumFrom 0)) [[1,2],[],[3,4,5],[6],[7,8]]
[[1,3],[],[6,10,15],[21],[28,36]]
Mostly useful if you want to feed an Auto
multiple inputs in the same
step. Note that if you always feed in singleton lists (lists with one
item), you'll more or less get the same behavior as normal.
Skipping
:: Monad m | |
=> a | default input value, during skipping periods |
-> Auto m a (b, Blip c) |
|
-> Auto m a ([b], c) |
Takes an Auto
that produces (b,
, and turns it into an
Blip
c)Auto
that produces ([b], c)
.
Basically, the new Auto
"squishes together" the periods of output
between each time the blip stream emits. All outputs between each
emitted value are accumulated and returned in the resulting [b]
.
It "does this" in the same manner as accelerateWith
and fastForward
:
first feed the input, then step repeatedly with the default input value.
>>>
let a :: Auto' Int (Int, Blip String)
a = proc i -> do sums <- sumFrom 0 -< i blp <- every 3 -< i -- emits every 3 ticks. id -< (sums, sums <& blp) -- replace emitted value -- with the running sum>>>
let skipA :: Auto' Int ([Int], String)
skipA = skipTo (-1) a>>>
let (res1, skipA') = stepAuto' skipA 8
>>>
res1
([8,7,6], 6) -- fed 8 first, then (-1) repeatedly>>>
let (res2, _ ) = evalAuto skipA' 5
>>>
res2
([11,10,9], 9) -- fed 5 first, then (-1) repeatedly
If the blip stream never emits then stepping this and getting the result
or the next/updated Auto
never terminates...so watch out!
:: Monad m | |
=> a | default input |
-> Interval m a b |
|
-> Auto m a b |
Turns an
into an Interval
m a b
--- that is, an
Auto
m a b
into an Auto
m a (Maybe b)
.Auto
m a b
It does this by "skipping over" all "off"/Nothing
input. When the
result "should" be a Nothing
, it re-runs the Interval
over and over
again with the given default input until the Auto
turns back "on"
again (outputs a Just
).
If the Interval
reaches a point where it will never be "on" again,
stepping this and getting the result or the next/updated Auto
won't
terminate...so watch out!
>>>
let a1 = offFor 3 . sumFrom 0
>>>
streamAuto' a1 [1..10]
[Nothing, Nothing, Nothing, Just 10, Just 15, Just 21]>>>
streamAuto' (fastForward 0 a1) [1..6]
[1,3,6,10,15,21]>>>
streamAuto' (fastForward (-10) a1) [1..6]
[-29,-27,-24,-20,-15,-9]
In that last example, the first input is 1, then it inputs (-10) until
it is "on"/Just
again (on the fourth step). Then continues imputing
2, 3, 4 etc.