Copyright | (c) Justin Le 2015 |
---|---|
License | MIT |
Maintainer | justin@jle.im |
Stability | unstable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module serves as the main entry point for the library; these are all basically re-exports. The re-exports are chosen so you can start doing "normal things" off the bat, including all of the types used in this library.
Conspicuously missing are the most of the tools for working with
Interval
, Blip
streams, switches, and the "collection" autos; those
are all pretty heavy, and if you do end up working with any of those
tools, simply importing the appropriate module should give you all you
need.
See the tutorial if you need help getting started!
- data Auto m a b
- type Auto' = Auto Identity
- data Blip a
- type Interval m a b = Auto m a (Maybe b)
- type Interval' a b = Auto' a (Maybe b)
- stepAuto :: Monad m => Auto m a b -> a -> m (b, Auto m a b)
- stepAuto' :: Auto' a b -> a -> (b, Auto' a b)
- evalAuto :: Monad m => Auto m a b -> a -> m b
- evalAuto' :: Auto' a b -> a -> b
- streamAuto :: Monad m => Auto m a b -> [a] -> m [b]
- streamAuto' :: Auto' a b -> [a] -> [b]
- stepAutoN :: Monad m => Int -> Auto m a b -> a -> m ([b], Auto m a b)
- stepAutoN' :: Int -> Auto' a b -> a -> ([b], Auto' a b)
- encodeAuto :: Auto m a b -> ByteString
- decodeAuto :: Auto m a b -> ByteString -> Either String (Auto m a b)
- readAuto :: FilePath -> Auto m a b -> IO (Either String (Auto m a b))
- writeAuto :: FilePath -> Auto m a b -> IO ()
- forcer :: NFData a => Auto m a a
- seqer :: Auto m a a
- hoistA :: (Monad m, Monad m') => (forall c. m c -> m' c) -> Auto m a b -> Auto m' a b
- generalizeA :: Monad m => Auto' a b -> Auto m a b
- arrM :: (a -> m b) -> Auto m a b
- arrD :: Serialize b => (a -> b) -> b -> Auto m a b
- accum :: Serialize b => (b -> a -> b) -> b -> Auto m a b
- accum_ :: (b -> a -> b) -> b -> Auto m a b
- accumM :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b
- accumM_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b
- accumD :: Serialize b => (b -> a -> b) -> b -> Auto m a b
- accumD_ :: (b -> a -> b) -> b -> Auto m a b
- accumMD :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b
- accumMD_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b
- mkState :: Serialize s => (a -> s -> (b, s)) -> s -> Auto m a b
- mkStateM :: Serialize s => (a -> s -> m (b, s)) -> s -> Auto m a b
- mkState_ :: (a -> s -> (b, s)) -> s -> Auto m a b
- mkStateM_ :: (a -> s -> m (b, s)) -> s -> Auto m a b
- effect :: m b -> Auto m a b
- iterator :: Serialize b => (b -> b) -> b -> Auto m a b
- iterator_ :: (b -> b) -> b -> Auto m a b
- iteratorM :: (Serialize b, Monad m) => (b -> m b) -> b -> Auto m a b
- iteratorM_ :: Monad m => (b -> m b) -> b -> Auto m a b
- sumFrom :: (Serialize a, Num a) => a -> Auto m a a
- sumFrom_ :: Num a => a -> Auto m a a
- sumFromD :: (Serialize a, Num a) => a -> Auto m a a
- sumFromD_ :: Num a => a -> Auto m a a
- productFrom :: (Serialize a, Num a) => a -> Auto m a a
- productFrom_ :: Num a => a -> Auto m a a
- mappender :: (Serialize a, Monoid a) => Auto m a a
- mappender_ :: Monoid a => Auto m a a
- mappendFrom :: (Serialize a, Semigroup a) => a -> Auto m a a
- lastVal :: Serialize a => a -> Auto m a a
- lastVal_ :: a -> Auto m a a
- count :: (Serialize b, Num b) => Auto m a b
- (-->) :: Monad m => Interval m a b -> Auto m a b -> Auto m a b
- emitJusts :: (a -> Maybe b) -> Auto m a (Blip b)
- emitOn :: (a -> Bool) -> Auto m a (Blip a)
- fromBlips :: a -> Auto m (Blip a) a
- fromBlipsWith :: b -> (a -> b) -> Auto m (Blip a) b
- holdWith :: Serialize a => a -> Auto m (Blip a) a
- holdWith_ :: a -> Auto m (Blip a) a
- perBlip :: Monad m => Auto m a b -> Auto m (Blip a) (Blip b)
- never :: Auto m a (Blip b)
- immediately :: Auto m a (Blip a)
- onFor :: Int -> Interval m a a
- during :: Monad m => Auto m a b -> Auto m (Maybe a) (Maybe b)
- off :: Interval m a b
- toOn :: Interval m a a
- interactAuto :: Interval' String String -> IO (Interval' String String)
- interactRS :: (Read a, Show b) => Interval' a b -> IO (Interval' String String)
- module Control.Applicative
- module Control.Arrow
- module Control.Category
- module Data.Functor.Identity
- module Data.Semigroup
Types
Auto
The Auto
type. For this library, an Auto
semantically
representsdenotes a a relationship/ between an input and an
output that is preserved over multiple steps, where that relationship is
(optionally) maintained within the context of a monad.
A lot of fancy words, I know...but you can think of an Auto
as nothing
more than a "stream transformer". A stream of sequential inputs come in
one at a time, and a stream of outputs pop out one at a time, as well.
Using the streamAuto
function, you can "unwrap" the inner stream
transformer from any Auto
: if a ::
, Auto
m a bstreamAuto
lets
you turn it into an [a] -> m [b]
. "Give me a stream of a
s, one at
a time, and I'll give you a list of b
s, matching a relationship to
your stream of a
s."
-- unwrap your inner [a] -> m [b]!streamAuto
:: Monad m =>Auto
m a b -> ([a] -> m [b])
There's a handy type synonym Auto'
for relationships that don't really
need a monadic context; the m
is just Identity
:
type Auto' = Auto Identity
So if you had an a ::
, you can use Auto'
a bstreamAuto'
to
"unwrap" the inner stream transformer, [a] -> [b]
.
-- unwrap your inner [a] -> [b]!streamAuto'
::Auto'
a b -> ([a] -> [b])
All of the Auto
s given in this library maintain some sort of semantic
relationship between streams --- for some, the outputs might be the
inputs with a function applied; for others, the outputs might be the
cumulative sum of the inputs.
See the tutorial for more information!
Operationally, an
is implemented as a "stateful
function". A function from an Auto
m a ba
where, every time you "apply" it, you
get a b
and an "updated Auto
"/function with updated state.
You can get this function using stepAuto
:
stepAuto
::Auto
m a b -> (a -> m (b,Auto
m a b))
stepAuto'
::Auto'
a b -> (a -> (b,Auto'
a b))
"Give me an a
and I'll give you a b
and your "updated" Auto
".
Auto
s really are mostly useful because they can be composed, chained,
and modified using their various typeclass instances, like Category
,
Applicative
, Functor
, Arrow
, etc., and also with the combinators
in this library. You can build complex programs as a complex Auto
by
building up smaller and smaller components. See the tutorial for more
information on this.
This type also contains information on its own serialization, so you can
serialize and re-load the internal state to binary or disk. See the
"serialization" section in the documentation for Control.Auto.Core, or
the documentation for mkAutoM
for more details.
Monad m => Category * (Auto m) | Gives the ability to "compose" two |
Monad m => Arrow (Auto m) | Gives us
Also allows you to have an
Most importantly, however, allows for "proc" notation; see the tutorial! for more details. |
Monad m => ArrowChoice (Auto m) | Allows you to have an
Again mostly useful for "proc" notation, with branching. |
MonadFix m => ArrowLoop (Auto m) | Finds the fixed point of self-referential |
Monad m => Strong (Auto m) | See |
Monad m => Choice (Auto m) | See |
MonadFix m => Costrong (Auto m) | See |
Monad m => Profunctor (Auto m) |
|
(Monad m, Alternative m) => Alternative (Auto m a) | When the underlying 'Monad'/'Applicative'
|
Monad m => Functor (Auto m a) | Maps over the output stream of the
|
Monad m => Applicative (Auto m a) |
For effectful
would, for example, behave just like |
Typeable ((* -> *) -> * -> * -> *) Auto | |
(Monad m, Floating b) => Floating (Auto m a b) | A bunch of constant producers, mappers-of-output-streams, and forks-and-recombiners. |
(Monad m, Fractional b) => Fractional (Auto m a b) | Fork the input stream and divide the outputs. |
(Monad m, Num b) => Num (Auto m a b) | Fork the input stream and add, multiply, etc. the outputs.
|
(Monad m, IsString b) => IsString (Auto m a b) | String literals in code will be
|
(Monad m, Monoid b) => Monoid (Auto m a b) | Fork the input stream and mappend the outputs.
|
(Monad m, Semigroup b) => Semigroup (Auto m a b) | Fork the input stream and |
Misc
When used in the context of an input or output of an Auto
, a
represents a stream that occasionally, at "independent" or "discrete"
points, emits a value of type Blip
aa
.
Contrast this to Interval
, where things are meant to be "on" or "off"
for contiguous chunks at a time; blip streams are "blippy", and
Interval
s are "chunky".
It's here mainly because it's a pretty useful abstraction in the context
of the many combinators found in various modules of this library. If
you think of an
as producing a "blip stream",
then there are various combinators and functions that are specifically
designed to manipulate blip streams.Auto
m a (Blip
b)
For the purposes of the semantics of what Blip
is supposed to
represent, its constructors are hidden. (Almost) all of the various
Blip
combinators (and its very useful Functor
instance) "preserve
Blip
ness" --- one-at-a-time occurrences remain one-at-a-time under all
of these combinators, and you should have enough so that direct access
to the constructor is not needed.
If you are creating a framework, library, or backend, you might want to
manually create blip stream-producing Auto
s for your users to
access. In this case, you can import the constructors and useful
internal (and, of course, semantically unsafe) functions from
Control.Auto.Blip.Internal.
Functor Blip | |
Show a => Show (Blip a) | |
Generic (Blip a) | |
Semigroup a => Monoid (Blip a) | Merge two blip streams together; the result emits with either of the
two merged streams emit. When both emit at the same time, emit the
result of |
Serialize a => Serialize (Blip a) | |
NFData a => NFData (Blip a) | |
Semigroup a => Semigroup (Blip a) | Merge two blip streams together; the result emits with either of the
two merged streams emit. When both emit at the same time, emit the
result of |
Typeable (* -> *) Blip | |
type Rep (Blip a) |
type Interval m a b = Auto m a (Maybe b) Source
Represents a relationship between an input and an output, where the
output can be "on" or "off" (using Just
and Nothing
) for contiguous
chunks of time.
Just a type alias for
. If you ended up here
with a link...no worries! If you see Auto
m a (Maybe
b)
, just think
Interval
m a b
for type inference/type checking purposes.Auto
m a (Maybe
b)
If you see something of type Interval
, you can rest assured that it
has "interval semantics" --- it is on and off for meaningfully
contiguous chunks of time, instead of just on and off willy nilly. If
you have a function that expects an Interval
, then the function
expects its argument to behave in this way.
Working with Auto
Running
:: Monad m | |
=> Auto m a b | the |
-> a | the input |
-> m (b, Auto m a b) | the output, and the updated |
Runs the Auto
through one step.
That is, given an
, returns a function that takes an Auto
m a ba
and returns a b
and an "updated"/"next" Auto
; an a -> m (b,
.Auto
m a b)
This is the main way of running an Auto
"step by step", so if you have
some sort of game loop that updates everything every "tick", this is
what you're looking for. At every loop, gather input a
, feed it into
the Auto
, "render" the result b
, and get your new Auto
to run the
next time.
Here is an example with
, the sumFrom
0Auto
whose output is the
cumulative sum of the inputs, and an underying monad of Identity
.
Here,
stepAuto :: Auto Identity Int Int -> (Int -> Identity (Int, Auto Identity Int Int))
Every time you "step", you give it an Int
and get a resulting Int
(the cumulative sum) and the "updated Auto
", with the updated
accumulator.
>>>
let a0 :: Auto Identity Int Int
a0 = sumFrom 0>>>
let Identity (res1, a1) = stepAuto a0 4 -- run with 4
>>>
res1
4 -- the cumulative sum, 4>>>
let Identity (res2, a2) = stepAuto a1 5 -- run with 5
>>>
res2
9 -- the cumulative sum, 4 + 5>>>
let Identity (res3, _ ) = stepAuto a2 3 -- run with 3
>>>
res3
12 -- the cumulative sum, 4 + 5 + 3
By the way, for the case where your Auto
is under Identity
, we have
a type synomym Auto'
...and a convenience function to make "running" it
more streamlined:
>>>
let a0 :: Auto' Int Int
a0 = sumFrom 0>>>
let (res1, a1) = stepAuto' a0 4 -- run with 4
>>>
res1
4 -- the cumulative sum, 4>>>
let (res2, a2) = stepAuto' a1 5 -- run with 5
>>>
res2
9 -- the cumulative sum, 4 + 5>>>
let (res3, _ ) = stepAuto' a2 3 -- run with 3
>>>
res3
12 -- the cumulative sum, 4 + 5 + 3
But, if your Auto
actaully has effects when being stepped, stepAuto
will execute them:
>>>
let a0 :: Auto IO Int Int
a0 = effect (putStrLn "hey!") *> sumFrom 0>>>
(res1, a1) <- stepAuto a0 4 -- run with 4
hey! -- IO effect>>>
res1
4 -- the cumulative sum, 4>>>
(res2, a2) <- stepAuto a1 5 -- run with 5
hey! -- IO effect>>>
res2
9 -- the cumulative sum, 4 + 5>>>
(res3, _ ) <- stepAuto a2 3 -- run with 3
hey! -- IO effect>>>
res3
12 -- the cumulative sum, 4 + 5 + 3
(Here,
is an effect
(putStrLn
"hey")
, which
ignores its input and just executes Auto
IO Int ()
every time it is
run. When we use putStrLn
"hey"*>
from Control.Applicative, we "combine" the two
Auto
s together and run them both on each input (4, 5, 3...)...but
for the "final" output at the end, we only return the output of the
second one,
(5, 9, 12...))sumFrom
0
If you think of an
as a "stateful function" Auto
m a ba -> m b
,
then stepAuto
lets you "run" it.
In order to directly run an Auto
on a stream, an [a]
, use
streamAuto
. That gives you an [a] -> m [b]
.
Runs an Auto'
through one step.
That is, given an
, returns a function that takes an Auto'
a ba
and returns a b
and an "updated"/"next" Auto'
; an a -> (b,
.Auto'
a b)
See stepAuto
documentation for motivations, use cases, and more
details. You can use this instead of stepAuto
when your underyling
monad is Identity
, and your Auto
doesn't produce any effects.
Here is an example with
, the sumFrom
0Auto'
whose output is the
cumulative sum of the inputs
stepAuto' :: Auto' Int Int -> (Int -> (Int, Auto' Int Int))
Every time you "step", you give it an Int
and get a resulting Int
(the cumulative sum) and the "updated Auto'
", with the updated
accumulator.
>>>
let a0 :: Auto' Int Int
a0 = sumFrom 0>>>
let (res1, a1) = stepAuto' a0 4 -- run with 4
>>>
res1
4 -- the cumulative sum, 4>>>
let (res2, a2) = stepAuto' a1 5 -- run with 5
>>>
res2
9 -- the cumulative sum, 4 + 5>>>
let (res3, _ ) = stepAuto' a2 3 -- run with 3
>>>
res3
12 -- the cumulative sum, 4 + 5 + 3
If you think of an
as a "stateful function" Auto'
a ba -> b
,
then stepAuto'
lets you "run" it.
In order to directly run an Auto'
on a stream, an [a]
, use
streamAuto'
. That gives you an [a] -> [b]
.
Stream an Auto
over a list, returning the list of results. Does
this "lazily" (over the Monad), so with most Monads, this should work
fine with infinite lists.
Note that, conceptually, this turns an
into an Auto
m a b[a] ->
m [b]
.
See streamAuto'
for a simpler example; here is one taking advantage of
monadic effects:
>>>
let a = arrM print *> sumFrom 0 :: Auto IO Int Int
>>>
ys <- streamAuto a [1..5]
1 -- IO effects 2 3 4 5>>>
ys
[1,3,6,10,15] -- the result
a
here is like
, except at every step, prints the input
item to stdout as a side-effect.sumFrom
0
Stream an Auto'
over a list, returning the list of results. Does
this lazily, so this should work fine with (and is actually somewhat
designed for) infinite lists.
Note that conceptually this turns an
into an Auto'
a b[a] -> [b]
>>>
streamAuto' (arr (+3)) [1..10]
[4,5,6,7,8,9,10,11,12,13]>>>
streamAuto' (sumFrom 0) [1..5]
[1,3,6,10,15]>>>
streamAuto' (productFrom 1) . streamAuto' (sumFrom 0) $ [1..5]
[1,3,18,180,2700]>>>
streamAuto' (productFrom 1 . sumFrom 0) $ [1..5]
[1,3,18,180,2700]>>>
streamAuto' id [1..5]
[1,2,3,4,5]
:: Monad m | |
=> Int | number of times to step the |
-> Auto m a b | the |
-> a | the repeated input |
-> m ([b], Auto m a b) | list of outputs and the updated |
Streams (in the context of the underlying monad) the given Auto
with
a stream of constant values as input, a given number of times. After
the given number of inputs, returns the list of results and the
next/updated Auto
, in the context of the underlying monad.
stepAutoN n a0 x = overList a0 (replicate n x)
See stepAutoN'
for a simpler example; here is one taking advantage of
monadic effects:
>>>
let a = arrM print *> sumFrom 0 :: Auto IO Int Int
>>>
(ys, a') <- stepAutoN 5 a 3
3 -- IO effects 3 3 3 3>>>
ys
[3,6,9,12,15] -- the result>>>
(ys'', _) <- stepAutoN 5 a' 5
5 -- IO effects 5 5 5 5>>>
ys''
[20,25,30,35,50] -- the result
a
here is like
, except at every step, prints the input
item to stdout as a side-effect.sumFrom
0
:: Int | number of times to step the |
-> Auto' a b | the |
-> a | the repeated input |
-> ([b], Auto' a b) | list of outputs and the updated |
Streams the given Auto'
with a stream of constant values as input,
a given number of times. After the given number of inputs, returns the
list of results and the next/updated Auto
.
stepAutoN' n a0 x = overList' a0 (replicate n x)
>>>
let (ys, a') = stepAutoN' 5 (sumFrom 0) 3
>>>
ys
[3,6,9,12,15]>>>
let (ys', _) = stepAutoN' 5 a' 5
>>>
ys'
[20,25,30,35,40]
Serializing
See the header of the "serializing" section of Control.Auto.Core for more detail on how these work.
encodeAuto :: Auto m a b -> ByteString Source
Encode an Auto
and its internal state into a ByteString
.
decodeAuto :: Auto m a b -> ByteString -> Either String (Auto m a b) Source
Resume an Auto
from its ByteString
serialization, giving
a Left
if the deserialization is not possible.
Strictness
Internal monad
:: (Monad m, Monad m') | |
=> (forall c. m c -> m' c) | monad morphism; the natural transformation |
-> Auto m a b | |
-> Auto m' a b |
Swaps out the underlying Monad
of an Auto
using the given monad
morphism "transforming function", a natural transformation.
Basically, given a function to "swap out" any m a
with an m' a
, it
swaps out the underlying monad of the Auto
.
This forms a functor, so you rest assured in things like this:
hoistA id == id hoistA f a1 . hoistA f a2 == hoistA f (a1 . a2)
generalizeA :: Monad m => Auto' a b -> Auto m a b Source
Auto constructors
:: (a -> m b) | monadic function |
-> Auto m a b |
Applies the given "monadic function" (function returning a monadic action) to every incoming item; the result is the result of executing the action returned.
Note that this essentially lifts a "Kleisli arrow"; it's like arr
, but
for "monadic functions" instead of normal functions:
arr :: (a -> b) -> Auto m a b arrM :: (a -> m b) -> Auto m a b
arrM f . arrM g == arrM (f <=< g)
One neat trick you can do is that you can "tag on effects" to a normal
Auto
by using *>
from Control.Applicative. For example:
>>>
let a = arrM print *> sumFrom 0
>>>
ys <- streamAuto a [1..5]
1 -- IO output 2 3 4 5>>>
ys
[1,3,6,10,15] -- the result
Here, a
behaves "just like"
...except, when you step it,
it prints out to stdout as a side-effect. We just gave automatic
stdout logging behavior!sumFrom
0
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]
from Accumulators
Result-first
Construct an Auto
from a "folding" function: b -> a -> b
yields an
. Basically acts like a Auto
m a bfoldl
or a scanl
. There is
an internal accumulator that is "updated" with an a
at every step.
Must be given an initial accumulator.
Example: an Auto
that sums up all of its input.
>>>
let summer = accum (+) 0
>>>
let (sum1, summer') = stepAuto' summer 3
>>>
sum1
3>>>
let (sum2, summer'') = stepAuto' summer' 10
>>>
sum2
13>>>
streamAuto' summer'' [1..10]
[14,16,19,23,28,34,41,49,58,68]
If your accumulator b
does not have a Serialize
instance, then you
should either write a meaningful one, or throw away serializability and
use accum_
.
:: (b -> a -> b) | accumulating function |
-> b | intial accumulator |
-> Auto m a b |
:: (Serialize b, Monad m) | |
=> (b -> a -> m b) | (monadic) accumulating function |
-> b | initial accumulator |
-> Auto m a b |
Construct an Auto
from a "monadic" "folding" function: b -> a ->
m b
yields an
. Basically acts like a Auto
m a bfoldM
or scanM
(if it existed). here is an internal accumulator that is "updated" with
an input a
with the result of the executed m b
at every step. Must
be given an initial accumulator.
See accum
for more details.
If your accumulator b
does not have a Serialize
instance, then you
should either write a meaningful one, or throw away serializability and
use accumM_
.
Initial accumulator-first
A "delayed" version of accum
, where the first output is the initial
state of the accumulator, before applying the folding function. Useful
in recursive bindings.
>>>
let summerD = accumD (+) 0
>>>
let (sum1, summerD') = stepAuto' summerD 3
>>>
sum1
0>>>
let (sum2, summerD'') = stepAuto' summerD' 10
>>>
sum2
3>>>
streamAuto' summerD'' [1..10]
[13,14,16,19,23,28,34,41,49,58]
(Compare with the example in accum
)
:: (b -> a -> b) | accumulating function |
-> b | intial accumulator |
-> Auto m a b |
The non-resuming/non-serializing version of accumD
.
:: (Serialize b, Monad m) | |
=> (b -> a -> m b) | (monadic) accumulating function |
-> b | initial accumulator |
-> Auto m a b |
A "delayed" version of accumM
, where the first output is the initial
state of the accumulator, before applying the folding function. Useful
in recursive bindings.
The non-resuming/non-serializing version of accumMD
.
from State transformers
Construct an Auto
from a state transformer: an a -> s -> (b, s)
gives you an
, for any Auto
m a bMonad
m
. At every step, it
takes in the a
input, runs the function with the stored internal
state, returns the b
result, and now contains the new resulting state.
You have to intialize it with an initial state, of course.
From the "stream transformer" point of view, this is rougly equivalent
to mapAccumL
from Data.List, with the function's arguments and
results in the backwards order.
streamAuto' (mkState f s0) = snd . mapAccumL (\s x -> swap (f x s))
Try not to use this if it's ever avoidable, unless you're a framework
developer or something. Try make something by combining/composing the
various Auto
combinators.
If your state s
does not have a Serialize
instance, then you should
either write a meaningful one, provide the serialization methods
manually with mkState'
, or throw away serializability and use
mkState_
.
Construct an Auto
from a "monadic" state transformer: a -> s ->
m (b, s)
gives you an
. At every step, it takes in the
Auto
m a ba
input, runs the function with the stored internal state and
"executes" the m (b, s)
to get the b
output, and stores the s
as
the new, updated state. Must be initialized with an initial state.
Try not to use this if it's ever avoidable, unless you're a framework
developer or something. Try make something by combining/composing the
various Auto
combinators.
This version is a wrapper around mkAuto
, that keeps track of the
serialization and re-loading of the internal state for you, so you don't
have to deal with it explicitly.
If your state s
does not have a Serialize
instance, then you should
either write a meaningful one, provide the serialization methods
manually with mkStateM'
, or throw away serializability and use
mkStateM_
.
:: (a -> s -> (b, s)) | state transformer |
-> s | initial state |
-> Auto m a b |
:: (a -> s -> m (b, s)) | (monadic) state transformer |
-> s | initial state |
-> Auto m a b |
Generators
Effects
:: m b | monadic action to contually execute. |
-> Auto m a b |
To get every output, executes the monadic action and returns the result as the output. Always ignores input.
This is basically like an "effectful" pure
:
pure
:: b ->Auto
m a beffect
:: m b ->Auto
m a b
The output of pure
is always the same, and the output of effect
is
always the result of the same monadic action. Both ignore their inputs.
Fun times when the underling Monad
is, for instance, Reader
.
>>>
let a = effect ask :: Auto (Reader b) a b
>>>
let r = evalAuto a () :: Reader b b
>>>
runReader r "hello"
"hello">>>
runReader r 100
100
If your underling monad has effects (IO
, State
, Maybe
, Writer
,
etc.), then it might be fun to take advantage of *>
from
Control.Applicative to "tack on" an effect to a normal Auto
:
>>>
let a = effect (modify (+1)) *> sumFrom 0 :: Auto (State Int) Int Int
>>>
let st = streamAuto a [1..10]
>>>
let (ys, s') = runState st 0
>>>
ys
[1,3,6,10,15,21,28,36,45,55]>>>
s'
10
Out Auto
a
behaves exactly like
, except at each step,
it also increments the underlying/global state by one. It is sumFrom
0
with an "attached effect".sumFrom
0
Iterators
:: (b -> b) | iterating function |
-> b | starting value and initial output |
-> Auto m a b |
The non-resuming/non-serializing version of iterator
.
:: (Serialize b, Monad m) | |
=> (b -> m b) | (monadic) iterating function |
-> b | starting value and initial output |
-> Auto m a b |
Like iterator
, but with a monadic function.
:: Monad m | |
=> (b -> m b) | (monadic) iterating function |
-> b | starting value and initial output |
-> Auto m a b |
The non-resuming/non-serializing version of iteratorM
.
Common Auto
s and combinators
Processes
The stream of outputs is the cumulative/running sum of the inputs so far, starting with an initial count.
The first output takes into account the first input. See sumFromD
for
a version where the first output is the initial count itself.
sumFrom x0 = accum (+) x0
The non-resuming/non-serializing version of sumFrom
.
Like sumFrom
, except the first output is the starting count.
>>>
let a = sumFromD 5
>>>
let (y1, a') = stepAuto' a 10
>>>
y1
5>>>
let (y2, _ ) = stepAuto' a' 3
>>>
y2
10
>>>
streamAuto' (sumFrom 0) [1..10]
[1,3,6,10,15,21,28,36,45,55]>>>
streamAuto' (sumFromD 0) [1..10]
[0,1,3,6,10,15,21,28,36,45]
It's sumFrom
, but "delayed".
Useful for recursive bindings, where you need at least one value to be able to produce its "first output" without depending on anything else.
sumFromD x0 = sumFrom x0 . delay 0
sumFromD x0 = delay x0 . sumFrom x0
The non-resuming/non-serializing version of sumFromD
.
The output is the running/cumulative product of all of the inputs so far, starting from an initial product.
productFrom x0 = accum (*) x0
The non-resuming/non-serializing version of productFrom
.
mappender :: (Serialize a, Monoid a) => Auto m a a Source
The output is the running/cumulative mconcat
of all of the input
seen so far, starting with mempty
.
>>>
streamauto' mappender . map Last $ [Just 4, Nothing, Just 2, Just 3]
[Last (Just 4), Last (Just 4), Last (Just 2), Last (Just 3)]>>>
streamAuto' mappender ["hello","world","good","bye"]
["hello","helloworld","helloworldgood","helloworldgoodbye"]
mappender = accum mappend mempty
mappender_ :: Monoid a => Auto m a a Source
The non-resuming/non-serializing version of mappender
.
The output is the running <>
-sum (mappend
for Semigroup
) of all
of the input values so far, starting with a given starting value.
Basically like mappender
, but with a starting value.
>>>
streamAuto' (mappendFrom (Max 0)) [Max 4, Max (-2), Max 3, Max 10]
[Max 4, Max 4, Max 4, Max 10]
mappendFrom m0 = accum (<>) m0
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]
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]
Switches
:: Monad m | |
=> Interval m a b | initial behavior |
-> Auto m a b | final behavior, when the initial behavior turns off. |
-> Auto m a b |
"This, then that". Behave like the first Interval
(and run its
effects) as long as it is "on" (outputting Just
). As soon as it turns
off (is 'Nothing), it'll "switch over" and begin behaving like the
second Auto
forever, running the effects of the second Auto
, too.
Works well if the Auto
s follow interval semantics from
Control.Auto.Interval.
>>>
let a1 = whileI (<= 4) --> pure 0
>>>
streamAuto' a1 [1..10]
[1, 2, 3, 4, 0, 0, 0, 0, 0, 0]
(whileI
only lets items satisfying the predicate pass through as "on",
and is "off" otherwise; pure
is the Auto
that always produces the
same output)
Association works in a way that you can "chain" -->
s, as long as you
have an appropriate Auto
(and not Interval
) at the end:
>>>
let a2 = onFor 3 . sumFrom 0
--> onFor 3 . sumFrom 100 --> pure 0>>>
streamAuto' a2 [1..10]
[1,3,6,104,109,115,0,0,0,0]
a --> b --> c
associates as a --> (b --> c)
This is pretty invaluable for having Auto
s "step" through a series of
different Auto
s, progressing their state from one stage to the next.
Auto
s can control when they want to be "moved on" from by turning
"off" (outputting Nothing
).
Note that recursive bindings work just fine, so:
>>>
let a3 = onFor 2 . pure "hello"
--> onFor 2 . pure "goodbye" --> a3>>>
let (res3, _) = stepAutoN' 8 a3 ()
>>>
res3
["hello", "hello", "world", "world", "hello", "hello", "world", "world"]
the above represents an infinite loop between outputting "hello" and outputting "world".
For serialization, an extra byte cost is incurred per invocation of
-->
. For cyclic switches like a3
, every time the cycle "completes",
it adds another layer of -->
byte costs. For example, initially,
saving a3
incurs a cost for the two -->
s. After a3
loops once,
it incurs a cost for another two -->
s, so it costs four -->
s. After
a3
loops another time, it is like a cost of six -->
s. So be aware
that for cyclic bindings like a3
, space for serialization grows at
O(n).
By the way, it might be worth contrasting this with <|!>
and <|?>
from Control.Auto.Interval, which have the same type signatures.
Those alternative-y operators always feed the input to both sides,
run both sides, and output the first Just
. With <|!>
, you can
"switch back and forth" to the first Auto
as soon as the first Auto
is "on" (Just
) again.
-->
, in contrast, runs only the first Auto
until it is
off (Nothing
)...then runs only the second Auto
. This transition is
one-way, as well.
Blips
An Auto
that runs every input through a a ->
test and
produces a blip stream that emits the value inside every Maybe
bJust
result.
Particularly useful with prisms from the lens package, where things
like emitJusts (preview _Right)
will emit the b
whenever the input
Either a b
stream is a Right
.
Warning! Carries all of the same dangers of emitOn
. You can easily
break blip semantics with this if you aren't sure what you are doing.
Remember to only emit at discrete, separate occurences, and not for
interval-like (on and off for chunks at a time) things. For interval
semantics, we have Control.Auto.Interval.
See the examples of emitOn
for more concrete good/bad use cases.
Produces a blip stream that emits the input value whenever the input satisfies a given predicate.
Warning! This Auto
has the capability of "breaking" blip semantics.
Be sure you know what you are doing when using this. Blip streams are
semantically supposed to only emit at discrete, separate occurrences.
Do not use this for interval-like (on and off for chunks at a time)
things; each input should be dealt with as a separate thing.
For interval semantics, we have Interval
from Control.Auto.Interval.
Good example:
-- is only emitting at discrete blips emitOn even . iterator (+ 1) 0
Bad examples:
-- is emitting for "durations" or "intervals" of time. emitOn (< 10) . iterator (+ 1) 0 emitOn (const True) . foo
These bad examples would be good use cases of Interval
.
:: b | the 'default value" to output when the input is not emitting. |
-> (a -> b) | the function to apply to the emitted value whenever input is emitting. |
-> Auto m (Blip a) b |
is an fromBlipsWith
d fAuto
that decomposes the incoming blip
stream by constantly outputting d
except when the stream emits, and
outputs the result of applying f
to the emitted value when it does.
holdWith :: Serialize a => a -> Auto m (Blip a) a Source
is an holdWith
y0Auto
whose output is always the /most recently
emitted/ value from the input blip stream. Before anything is emitted,
y0
is outputted as a placeholder.
Contrast with hold
from Control.Auto.Interval.
perBlip :: Monad m => Auto m a b -> Auto m (Blip a) (Blip b) Source
Takes an
(an Auto
m a bAuto
that turns incoming a
s into
outputting b
s) into an
; the original
Auto
m (Blip
a) (Blip
b)Auto
is lifted to only be applied to emitted contents of a blip
stream.
When the stream emits, the original Auto
is "stepped" with the emitted
value; when it does not, it is paused and frozen until the next
emission.
>>>
let sums = perBlip (sumFrom 0)
>>>
let blps = eachAt 2 [1,5,2]
>>>
take 8 . streamAuto' blps $ repeat ()
[NoBlip, Blip 1, NoBlip, Blip 5, NoBlip, Blip 2, NoBlip, NoBlip]>>>
take 8 . streamAuto' (sums . blps) $ repeat ()
[NoBlip, Blip 1, NoBlip, Blip 6, NoBlip, Blip 8, NoBlip, NoBlip]
never :: Auto m a (Blip b) Source
An Auto
that ignores its input and produces a blip stream never
emits.
immediately :: Auto m a (Blip a) Source
Produces a blip stream that emits with the first received input value, and never again after that.
Often used with pure
:
immediately . pure "Emit me!"
Or, in proc notation:
blp <- immediately -< "Emit me!"
to get a blip stream that emits a given value (eg., "Emit me!") once and stops emitting ever again.
>>>
streamAuto' (immediately . pure "Emit me!") [1..5]
[Blip "Emit Me!", NoBlip, NoBlip, NoBlip, NoBlip]
Intervals
For
, the first onFor
nn
items in the output stream are always
"on" (passing through with exactly the value of the corresponding
input); for the rest, the output stream is always "off", suppressing all
input values forevermore.
If a number less than 0 is passed, 0 is used.
Lifts an
(transforming Auto
m a ba
s into b
s) into an
(or, Auto
m (Maybe
a) (Maybe
b)
,
transforming intervals of Interval
m (Maybe
a) ba
s into intervals of b
.
It does this by running the Auuto
as normal when the input is "on",
and freezing itbeing "off" when the input is off/.
>>>
let a1 = during (sumFrom 0) . onFor 2 . pure 1
>>>
take 5 . streamAuto' a1 $ repeat ()
[Just 1, Just 2, Nothing, Nothing, Nothing]
>>>
let a2 = during (sumFrom 0) . offFor 2 . pure 1
>>>
take 5 . streamAuto' a2 $ repeat ()
[Nothing, Nothing, Just 1, Just 2, Just 3]
(Remember that
is the pure
xAuto
that ignores its input and
constantly just pumps out x
at every step)
Note the difference between putting the sumFrom
"after" the
offFor
in the chain with during
(like the previous example)
and putting the sumFrom
"before":
>>>
let a3 = offFor 2 . sumFrom 0 . pure 1
>>>
take 5 . streamAuto' a3 $ repeat ()
[Nothing, Nothing, Just 3, Just 4, Just 5]
In the first case (with a2
), the output of
was suppressed
by pure
1offFor
, and
was only summing on the times
that the 1's were "allowed through"...so it only "starts counting" on
the third step.during
(sumFrom
0)
In the second case (with a3
), the output of the
is never
suppressed, and went straight into the pure
1
. sumFrom
0sumFrom
is
always summing, the entire time. The final output of that
is suppressed at the end with sumFrom
0
.offFor
2
The output stream is always on, with exactly the value of the corresponding input.
toOn == arr Just
Running
:: Interval' String String |
|
-> IO (Interval' String String) | final |
Run an Auto'
"interactively". Every step grab a string from stdin,
and feed it to the Interval'
. If the Interval'
is "off", ends the
session; if it is "on", then prints the output value to stdout and
repeat all over again.
If your Auto
outputs something other than a String
, you can use
fmap
to transform the output into a String
en-route (like
).fmap
show
If your Auto
takes in something other than a String
, you can lmap
a function to convert the input String
to whatever intput your Auto
expects.
You can use duringRead
or bindRead
if you have an Auto'
or
Interval'
that takes something read
able, to chug along until you
find something non-readable; there's also interactRS
which handles
most of that for you.
Outputs the final Interval'
when the interaction terminates.
Re-exports
module Control.Applicative
module Control.Arrow
module Control.Category
module Data.Functor.Identity
module Data.Semigroup