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

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

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 :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> [a] -> Auto m' a b -> t m b Source

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.