| Copyright | (c) Justin Le 2015 |
|---|---|
| License | MIT |
| Maintainer | justin@jle.im |
| Stability | unstable |
| Portability | portable |
| Safe Haskell | None |
| Language | Haskell2010 |
Control.Auto.Run
Contents
Description
This module provides utilities for "running" and "unrolling" Autos.
You'll find "enhanced" versions of stepAuto, mechanisms for running
Autos "interactively" inside IO, monadic and non-monadic
"self-runners" (provide the handlers, and the Auto just recursively
runs intself), and finally, ways of "unrolling" the underlying Monad
of Autos into more manageable and composable and easy to work with
forms.
- throughT :: (Monad m, Traversable t) => Auto m a b -> Auto m (t a) (t b)
- streamAuto :: Monad m => Auto m a b -> [a] -> m [b]
- streamAuto' :: Auto' a b -> [a] -> [b]
- overList :: Monad m => Auto m a b -> [a] -> m ([b], Auto m a b)
- overList' :: Auto' a b -> [a] -> ([b], Auto' a b)
- overTraversable :: (Monad m, Traversable t) => Auto m a b -> t a -> m (t b, Auto m a b)
- overTraversable' :: Traversable t => Auto' a b -> t a -> (t b, Auto' 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)
- evalAutoN :: Monad m => Int -> Auto m a b -> a -> m [b]
- evalAutoN' :: Int -> Auto' a b -> a -> [b]
- interactAuto :: Interval' String String -> IO (Interval' String String)
- interactRS :: (Read a, Show b) => Interval' a b -> IO (Interval' String String)
- interactM :: Monad m => (forall c. m c -> IO c) -> (b -> IO Bool) -> Auto m String b -> IO (Auto m String b)
- duringRead :: (Monad m, Read a) => Auto m a b -> Interval m String b
- bindRead :: (Monad m, Read a) => Interval m a b -> Interval m String b
- run :: Monad m => m a -> (b -> m (Maybe a)) -> Auto m a b -> m (Auto m a b)
- runM :: (Monad m, Monad m') => (forall c. m' c -> m c) -> m a -> (b -> m (Maybe a)) -> Auto m' a b -> m (Auto m' a b)
- runOnChan :: (b -> IO Bool) -> Chan a -> Auto' a b -> IO (Auto' a b)
- runOnChanM :: Monad m => (forall c. m c -> IO c) -> (b -> IO Bool) -> Chan a -> Auto m a b -> IO (Auto m a b)
- streamAutoEffects :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> [a] -> Auto m' a b -> t m b
- toEffectStream :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> m a -> Auto m' a b -> t m b
Lifting inputs and outputs
Arguments
| :: (Monad m, Traversable t) | |
| => Auto m a b |
|
| -> Auto m (t a) (t b) |
Lifts an to one that runs "through" a Auto m a bTraversable,
. It does this by running itself sequentially
over every element "in" the Auto m (t a) (t b)Traversable at every input.
This can be thought of as a polymorphic version of many other combinators in this library:
during = throughT :: Auto m a b -> Interval m (Maybe a) b perBlip = throughT :: Auto m a b -> Auto m (Blip a) (Blip b) accelOverList = throughT :: Auto m a b -> Auto m [a] [b]
The specialized versions will still be more performant, but this will be
more general...you can run the Auto through an input IntMap, for
example.
Note that this is actually an endofunctor on the Auto Category. That
is, for all Autos lifted and all lawful Traversables, usage should
obey the laws:
throughT id = id throughT g . throughT f = throughT (g . f)
Special stepAuto versions.
Streaming over lists
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. (That is, behaves
exactly like streamAuto (arrM f), and you can reason with mapM fAutos as if you'd
reason with mapM on an infinite list)
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
Note that we use "stream" here slightly differently than in libraries
like pipes or conduit. We don't stream over the m Monad (like
IO)...we stream over the input elements. Using streamAuto on an
infinite list allows you to "stop", for example, to find the
result...but it will still sequence all the *effects*.
For example:
>>>take 10 <$> streamAuto (arrM print *> id) [1..]
Will execute print on every element before "returning" with [1..10].
>>>flip runState 0 $ take 10 <$> streamAuto (arrM (modify . (+)) *> id) [1..]([1,2,3,4,5,6,7,8,9,10], .... (never terminates)
This will immediately return the "result", and you can bind to the
result with `(>>=)`, but it'll never return a "final state", because the
final state involves executing all of the modifys.
In other words, we stream values, not effects. You would analyze
this behavior the same way you would look at something like mapM.
If you want to stream effects, you can use streamAutoEffects or
toEffectStream, and use an effects streaming library like pipes (or
anything with ListT)...this will give the proper streaming of effects
with resource handling, handling infinite streams in finite space with
finite effects, etc.
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]
Arguments
| :: Monad m | |
| => Auto m a b | the |
| -> [a] | list of inputs to step the |
| -> m ([b], Auto m a b) | list of outputs and the updated |
Streams the Auto over a list of inputs; that is, "unwraps" the [a]
-> m [b] inside. Streaming is done in the context of the underlying
monad; when done consuming the list, the result is the list of outputs
updated/next Auto in the context of the underlying monad.
Basically just steps the Auto by feeding in every item in the list and
pops out the list of results and the updated/next Auto, monadically
chaining the steppings.
See overList' for a simpler example; the following example uses
effects from IO to demonstrate the monadic features of overList.
>>>let a = arrM print *> sumFrom 0 :: Auto IO Int Int>>>(ys, a') <- overList a [1..5]1 -- IO effects 2 3 4 5>>>ys[1,3,6,10,15]>>>(ys', _) <- overList a' [11..15]11 -- IO effects 12 13 14 15>>>ys'[26,38,51,65,80]
a is like , except at every step, prints the input item
to stdout as a side-effect. Note that in executing we get the updated
sumFrom 0a', which ends up with an accumulator of 15. Now, when we stream
a', we pick up were we left off (from 15) on the results.
Arguments
| :: Auto' a b | the |
| -> [a] | list of inputs to step the |
| -> ([b], Auto' a b) | list of outputs and the updated |
Streams an Auto' over a list of inputs; that is, "unwraps" the [a]
-> [b] inside. When done comsuming the list, returns the outputs and
the updated/next Auto'.
>>>let (ys, updatedSummer) = overList' (sumFrom 0) [1..5]>>>ys[1, 3, 6, 10, 15]>>>let (ys', _) = streamAuto' updatedSummer [1..5]>>>ys'[16, 18, 21, 25, 30]
If you wanted to stream over an infinite list then you don't care about
the Auto' at the end, and probably want streamAuto'.
Arguments
| :: (Monad m, Traversable t) | |
| => Auto m a b | the |
| -> t a |
|
| -> m (t b, Auto m a b) |
|
Like overList, but "streams" the Auto over all elements of any
Traversable, returning the final updated Auto.
Arguments
| :: Traversable t | |
| => Auto' a b | the |
| -> t a |
|
| -> (t b, Auto' a b) |
|
Running over one item repetitively
Arguments
| :: 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 33 -- IO effects 3 3 3 3>>>ys[3,6,9,12,15] -- the result>>>(ys'', _) <- stepAutoN 5 a' 55 -- 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
Arguments
| :: 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]
Arguments
| :: Monad m | |
| => Int | number of times to step the |
| -> Auto m a b | the |
| -> a | the repeated input |
| -> m [b] | list of outputs |
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 in the context
of the underlying monad.
Like stepAutoN, but drops the "next Auto". Only returns the list
of results.
>>>let a = arrM print *> sumFrom 0 :: Auto IO Int Int>>>ys <- evalAutoN 5 a 33 -- IO effects 3 3 3 3>>>ys[3,6,9,12,15] -- the result
a here is like , except at every step, prints the input
item to stdout as a side-effect.sumFrom 0
Arguments
| :: Int | number of times to step the |
| -> Auto' a b | the |
| -> a | the repeated input |
| -> [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.
Like stepAutoN', but drops the "next Auto'". Only returns the list
of results.
>>>evalAutoN' 5 (sumFrom 0) 3[3,6,9,12,15]
Running "interactively"
Arguments
| :: 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 readable, 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.
Arguments
| :: Monad m | |
| => (forall c. m c -> IO c) | natural transformation from the underlying |
| -> (b -> IO Bool) | function to "handle" each succesful |
| -> Auto m String b |
|
| -> IO (Auto m String b) | final |
Like interact, but much more general. You can run it with an Auto
of any underlying Monad, as long as you provide the natural
transformation from that Monad to IO.
The Auto can any ; you have to provide
a function to "handle" it yourself; a Maybe bb -> . You can print
the result, or write the result to a file, etc.; the IO BoolBool parameter
determines whether or not to "continue running", or to stop and return
the final updated Auto.
Helpers
Arguments
| :: (Monad m, Read a) | |
| => Auto m a b |
|
| -> Interval m String b |
Turn an Auto that takes a "readable" a and outputs a b into an
Auto that takes a String and outputs a . When the
Maybe bString is successfuly readable as the a, it steps the Auto and
outputs a succesful Just result; when it isn't, it outputs a Nothing
on that step.
>>>let a0 = duringRead (accum (+) (0 :: Int))>>>let (y1, a1) = stepAuto' a0 "12">>>y1Just 12>>>let (y2, a2) = stepAuto' a1 "orange">>>y2Nothing>>>let (y3, _ ) = stepAuto' a2 "4">>>y3Just 16
See interact for neat use cases.
Generalized "self-runners"
Arguments
| :: Monad m | |
| => m a | action to retrieve starting input |
| -> (b -> m (Maybe a)) | handling output and next input in |
| -> Auto m a b | |
| -> m (Auto m a b) | return the ran/updated |
Heavy duty abstraction for "self running" an Auto. Give a starting
input action, a (possibly side-effecting) function from an output to
the next input to feed in, and the Auto, and you get a feedback
loop that constantly feeds back in the result of the function applied to
the previous output. Stops when the "next input" function returns
Nothing.
Note that the none of the results are actually returned from the loop. Instead, if you want to process the results, they must be utilized in the "side-effects' of the "next input" function. (ie, a write to a file, or an accumulation to a state).
Arguments
| :: (Monad m, Monad m') | |
| => (forall c. m' c -> m c) | natural transformation from |
| -> m a | action to retrieve starting input |
| -> (b -> m (Maybe a)) | handling output and next input in |
| -> Auto m' a b |
|
| -> m (Auto m' a b) | return the resulting/run Auto in |
Running on concurrent channels
Running on as a ListT-compatible stream
Arguments
| :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') | |
| => (forall c. m' c -> m c) | function to change the underyling monad from |
| -> [a] | inputs to stream on |
| -> Auto m' a b |
|
| -> t m b | ListT-like structure sequencing effects |
Turns an Auto m' a b with a list of inputs into a "ListT compatible
effectful stream", as described at
http://www.haskellforall.com/2014/11/how-to-build-library-agnostic-streaming.html
Any library that offers a "ListT" type can use this result...and
usually turn it into an effectful stream.
For example, the pipes library offers runListT so you can run this,
running the Auto over the input list, all with the effect stream
manipulation tools and resource handling of pipes.
This is useful because auto, the library, mainly provides tools for working with transformers for value streams, and not effect streams or streams of effects. Using this, you can potentially have the best of both worlds.
Arguments
| :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') | |
| => (forall c. m' c -> m c) | function to change the underyling monad from |
| -> m a | action to generate inputs |
| -> Auto m' a b | Auto to run as an effectful stream |
| -> t m b |
|
Turns an Auto m' a b and an "input producer" m a into a "ListT
compatible effectful stream", as described at
http://www.haskellforall.com/2014/11/how-to-build-library-agnostic-streaming.html
Any library that offers a "ListT" type can use this result...and
usually turn it into an effectful stream.
For example, the pipes library offers runListT so you can run this,
constantly pulling out as from the stream using the m a, feeding
it in, and moving forward, all with the effect stream manipulation tools
and resource handling of pipes.
This is useful because auto, the library, mainly provides tools for working with transformers for value streams, and not effect streams or streams of effects. Using this, you can potentially have the best of both worlds.