auto-0.4.3.0: Denotative, locally stateful programming DSL & platform

Copyright(c) Justin Le 2015
LicenseMIT
Maintainerjustin@jle.im
Stabilityunstable
Portabilityportable
Safe HaskellNone
LanguageHaskell2010

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.

Synopsis

Lifting inputs and outputs

throughT Source #

Arguments

:: (Monad m, Traversable t) 
=> Auto m a b

Auto to run "through" a Traversable

-> Auto m (t a) (t b) 

Lifts an Auto m a b to one that runs "through" a Traversable, Auto m (t a) (t b). It does this by running itself sequentially over every element "in" the 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

streamAuto Source #

Arguments

:: Monad m 
=> Auto m a b

Auto to stream

-> [a]

input stream

-> m [b]

output stream

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, streamAuto (arrM f) behaves exactly like mapM f, and you can reason with Autos as if you'd reason with mapM on an infinite list)

Note that, conceptually, this turns an Auto m a b into an [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 sumFrom 0, except at every step, prints the input item to stdout as a side-effect.

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.

streamAuto' Source #

Arguments

:: Auto' a b

Auto' to stream

-> [a]

input stream

-> [b]

output stream

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 Auto' a b into an [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]

overList Source #

Arguments

:: Monad m 
=> Auto m a b

the Auto to run

-> [a]

list of inputs to step the Auto with

-> m ([b], Auto m a b)

list of outputs and the updated Auto

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 sumFrom 0, except at every step, prints the input item to stdout as a side-effect. Note that in executing we get the updated a', 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.

overList' Source #

Arguments

:: Auto' a b

the Auto' to run

-> [a]

list of inputs to step the Auto' with

-> ([b], Auto' a b)

list of outputs and the updated Auto'

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'.

overTraversable Source #

Arguments

:: (Monad m, Traversable t) 
=> Auto m a b

the Auto to run

-> t a

Traversable of inputs to step the Auto with

-> m (t b, Auto m a b)

Traversable of outputs and the updated Auto

Like overList, but "streams" the Auto over all elements of any Traversable, returning the final updated Auto.

overTraversable' Source #

Arguments

:: Traversable t 
=> Auto' a b

the Auto' to run

-> t a

Traversable of inputs to step the Auto' with

-> (t b, Auto' a b)

Traversable of outputs and the updated Auto'

Like overList', but "streams" the Auto' over all elements of any Traversable', returning the final updated Auto'.

Running over one item repetitively

stepAutoN Source #

Arguments

:: Monad m 
=> Int

number of times to step the Auto

-> Auto m a b

the Auto to run

-> a

the repeated input

-> m ([b], Auto m a b)

list of outputs and the updated Auto

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 sumFrom 0, except at every step, prints the input item to stdout as a side-effect.

stepAutoN' Source #

Arguments

:: Int

number of times to step the Auto'

-> Auto' a b

the Auto' to run

-> a

the repeated input

-> ([b], Auto' a b)

list of outputs and the updated Auto'

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]

evalAutoN Source #

Arguments

:: Monad m 
=> Int

number of times to step the Auto

-> Auto m a b

the Auto to run

-> 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 sumFrom 0, except at every step, prints the input item to stdout as a side-effect.

evalAutoN' Source #

Arguments

:: Int

number of times to step the Auto'

-> Auto' a b

the Auto' to run

-> a

the repeated input

-> [b]

list of outputs and the updated Auto'

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"

interactAuto Source #

Arguments

:: Interval' String String

Interval' to run interactively

-> IO (Interval' String String)

final Interval' after it all

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.

interactRS Source #

Arguments

:: (Read a, Show b) 
=> Interval' a b

Interval' to run interactively

-> IO (Interval' String String)

final Interval' after it all

Like interact, but instead of taking Interval' String String, takes any Interval' a b as long as a is Read and b is Show.

Will "stop" if either (1) the input is not read-able or (2) the Interval' turns off.

Outputs the final Auto' when the interaction terminates.

interactM Source #

Arguments

:: Monad m 
=> (forall c. m c -> IO c)

natural transformation from the underlying Monad of the Auto to IO

-> (b -> IO Bool)

function to "handle" each succesful Auto output

-> Auto m String b

Auto to run "interactively"

-> IO (Auto m String b)

final Auto after it all

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 Maybe b; you have to provide a function to "handle" it yourself; a b -> IO Bool. You can print the result, or write the result to a file, etc.; the Bool parameter determines whether or not to "continue running", or to stop and return the final updated Auto.

Helpers

duringRead Source #

Arguments

:: (Monad m, Read a) 
=> Auto m a b

Auto taking in a readable a, outputting b

-> Interval m String b

Auto taking in String, outputting Maybe b

Turn an Auto that takes a "readable" a and outputs a b into an Auto that takes a String and outputs a Maybe b. When the String 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.

bindRead Source #

Arguments

:: (Monad m, Read a) 
=> Interval m a b

Auto taking in a readable a, outputting Maybe b

-> Interval m String b

Auto taking in String, outputting Maybe b

Like duringRead, but the original Auto would output a Maybe b instead of a b. Returns Nothing if either the String fails to parse or if the original Auto returned Nothing; returns Just if the String parses and the original Auto returned Just.

See interact for neat use cases.

Generalized "self-runners"

run Source #

Arguments

:: Monad m 
=> m a

action to retrieve starting input

-> (b -> m (Maybe a))

handling output and next input in m

-> Auto m a b

Auto

-> m (Auto m a b)

return the ran/updated Auto in m

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).

runM Source #

Arguments

:: (Monad m, Monad m') 
=> (forall c. m' c -> m c)

natural transformation from m' (the Auto monad) to m (the running monad)

-> m a

action to retrieve starting input

-> (b -> m (Maybe a))

handling output and next input in m

-> Auto m' a b

Auto in monad m'

-> m (Auto m' a b)

return the resulting/run Auto in m

A generalized version of run where the Monad you are "running" the Auto in is different than the Monad underneath the Auto. You just need to provide the natural transformation.

Running on concurrent channels

runOnChan Source #

Arguments

:: (b -> IO Bool)

function to "handle" each succesful Auto output; result is whether or not to continue.

-> Chan a

Chan queue to pull input from.

-> Auto' a b

Auto' to run

-> IO (Auto' a b)

final Auto after it all, when the handle resturns False

Runs the Auto' in IO with inputs read from a Chan queue, from Control.Concurrency.Chan. It'll block until the Chan has a new input, run the Auto with the received input, process the output with the given handling function, and start over if the handling function returns True.

runOnChanM Source #

Arguments

:: Monad m 
=> (forall c. m c -> IO c)

natural transformation from the underling Monad of the Auto to IO

-> (b -> IO Bool)

function to "handle" each succesful Auto output; result is whether or not to continue.

-> Chan a

Chan queue to pull input from.

-> Auto m a b

Auto to run

-> IO (Auto m a b)

final Auto after it all, when the handle resturns False

A generalized version of runOnChan that can run on any Auto m; all that is required is a natural transformation from the underyling Monad m to IO.

Running on as a ListT-compatible stream

streamAutoEffects Source #

Arguments

:: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') 
=> (forall c. m' c -> m c)

function to change the underyling monad from m' to m

-> [a]

inputs to stream on

-> Auto m' a b

Auto to stream

-> 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.

toEffectStream Source #

Arguments

:: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') 
=> (forall c. m' c -> m c)

function to change the underyling monad from m' to m

-> m a

action to generate inputs

-> Auto m' a b

Auto to run as an effectful stream

-> t m b

ListT-compatible type

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.