Copyright | (c) Justin Le 2015 |
---|---|
License | MIT |
Maintainer | justin@jle.im |
Stability | unstable |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides utilities for "running" and "unrolling" Auto
s.
You'll find "enhanced" versions of stepAuto
, mechanisms for running
Auto
s "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 Auto
s 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
:: (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 Auto
s lifted and all lawful Traversable
s, 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
fAuto
s 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 modify
s.
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]
:: 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.
:: 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'
.
:: (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
.
:: Traversable t | |
=> Auto' a b | the |
-> t a |
|
-> (t b, Auto' a b) |
|
Running over one item repetitively
:: 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]
:: 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 3
3 -- 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
:: 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"
:: 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.
:: 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
Bool
Bool
parameter
determines whether or not to "continue running", or to stop and return
the final updated Auto
.
Helpers
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"
>>>
y1
Just 12>>>
let (y2, a2) = stepAuto' a1 "orange"
>>>
y2
Nothing>>>
let (y3, _ ) = stepAuto' a2 "4"
>>>
y3
Just 16
See interact
for neat use cases.
Generalized "self-runners"
:: 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).
Running on concurrent channels
Running on as a ListT-compatible stream
:: (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.
:: (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 a
s 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.