auto 0.4.0.0 → 0.4.1.0
raw patch · 10 files changed
+285/−61 lines, 10 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Control.Auto.Effects: sealReaderM :: Monad m => Auto (ReaderT r m) a b -> m r -> Auto m a b
+ Control.Auto.Effects: sealReaderMVar :: MonadIO m => Auto (ReaderT r m) a b -> MVar r -> Auto m a b
+ Control.Auto.Run: overTraversable :: (Monad m, Traversable t) => Auto m a b -> t a -> m (t b, Auto m a b)
+ Control.Auto.Run: overTraversable' :: Traversable t => Auto' a b -> t a -> (t b, Auto' a b)
+ Control.Auto.Run: throughT :: (Monad m, Traversable t) => Auto m a b -> Auto m (t a) (t b)
- Control.Auto: during :: Monad m => Auto m a b -> Auto m (Maybe a) (Maybe b)
+ Control.Auto: during :: Monad m => Auto m a b -> Interval m (Maybe a) b
- Control.Auto.Blip: became' :: Monad m => (a -> Bool) -> Auto m a (Blip ())
+ Control.Auto.Blip: became' :: (a -> Bool) -> Auto m a (Blip ())
- Control.Auto.Blip: became_ :: Monad m => (a -> Bool) -> Auto m a (Blip a)
+ Control.Auto.Blip: became_ :: (a -> Bool) -> Auto m a (Blip a)
- Control.Auto.Blip: noLonger' :: Monad m => (a -> Bool) -> Auto m a (Blip ())
+ Control.Auto.Blip: noLonger' :: (a -> Bool) -> Auto m a (Blip ())
- Control.Auto.Blip: noLonger_ :: Monad m => (a -> Bool) -> Auto m a (Blip a)
+ Control.Auto.Blip: noLonger_ :: (a -> Bool) -> Auto m a (Blip a)
- Control.Auto.Interval: during :: Monad m => Auto m a b -> Auto m (Maybe a) (Maybe b)
+ Control.Auto.Interval: during :: Monad m => Auto m a b -> Interval m (Maybe a) b
Files
- CHANGELOG.md +41/−0
- README.md +32/−18
- auto.cabal +4/−2
- src/Control/Auto/Blip.hs +56/−15
- src/Control/Auto/Core.hs +0/−1
- src/Control/Auto/Effects.hs +70/−10
- src/Control/Auto/Interval.hs +9/−9
- src/Control/Auto/Run.hs +71/−4
- src/Control/Auto/Time.hs +1/−1
- tutorial/tutorial.md +1/−1
CHANGELOG.md view
@@ -1,3 +1,44 @@+0.4.1.0+-------+<https://github.com/mstksg/auto/releases/tag/v0.4.1.0>++* Adapted to more consistent semantic versioning scheme, where the third+ number is a new update, and the fourth number is reserved for bug fixes.+* **Control.Auto.Blip**: `foldrB` and `foldlB'` officially **deprecated** in+ their current forms. From `0.5`, they will have corrected functionality+ and a new type signature. The current functionality doesn't really make+ sense, and was a mistake during their implementation. You can begin using+ the new versions now, with:++ ```+ foldrB = foldr (merge f) mempty+ foldlB' = foldl' (merge f) mempty+ ```+* **Control.Auto.Effects**: New "sealing" mechanisms for underlying+ `Reader`: `sealReaderMVar` and `sealReaderM`. `sealReaderMVar` allows+ things like "hot swapping" configuration data; at every step, the `Auto`+ asks for its environment from an `MVar`, that could be changed/modified+ from a different thread with new configuration data. `sealReaderM` is a+ more general/potentially dangerous version where the environment is+ retrieved through an arbitrary action in the underlying monad.+* **Control.Auto.Run**: New powerful combinator `throughT`, letting you+ "lift" an `Auto` to run over/through any `Traversable`. Can replace+ `during`, `perBlip`, `accelOverList`, etc. The specialized versions will+ remain more performant, though.+* **Control.Auto.Run**: In the spirit of the hip and current Foldable+ Traversable Proposal, `overTraversable` added to complement `overList`, so+ you can now "stream" `Auto`s over `IntMap`s, `Maybe`s, `Const`s...or any+ `Traversable`. Not replacing `overList` completely, though, for+ performance reasons.+* **Control.Auto.Blip**: Removed unnecessary `Monad` constraints on+ `became_`, `became'`, `noLonger_`, and `noLonger'`.+* **Control.Auto.Interval**: Bug fix on `holdFor` and `holdFor_`, where they+ had the potential to overflow `Int` and begin "holding" forever when+ given specifically malformed input.+* **Control.Auto.Time**: Performance boost on `accelOverList` by using+ strict `Writer` over lazy.++ 0.4.0.0 ------- <https://github.com/mstksg/auto/releases/tag/v0.4.0.0>
README.md view
@@ -67,12 +67,42 @@ **Auto** is a Haskell DSL and platform providing an API with declarative, compositional, denotative semantics for discrete-step, locally stateful, interactive programs, games, and automations, with implicitly derived-serialization. It is suited for any domain where your program's input or+serialization.++It is suited for any domain where your program's input or output is a stream of values, input events, or output views. At the high-level, it allows you to describe your interactive program or simulation as a *value stream transformer*, by composition and transformation of other-stream transformers.+stream transformers. So, things like: +1. Chat bots+2. Turn-based games+3. GUIs+4. Numerical simulations+5. Process controllers+6. Text-based interfaces+7. (Value) stream transformers, filters, mergers, processors++It's been called "FRP for discrete time contexts".++Intrigued? Excited? Start at [the tutorial][tutorial]!++[tutorial]: https://github.com/mstksg/auto/blob/master/tutorial/tutorial.md++It's a part of this package directory and also on github at the above link.+The current development documentation server is found at+<https://mstksg.github.io/auto>.++From there, you can check out my [All About Auto][aaa] series on my blog,+where I break sample projects and show to approach projects in real life. You+can also find examples and demonstrations in the [auto-examples][] repo on+github.++[aaa]: http://blog.jle.im/entries/series/+all-about-auto+[auto-examples]: https://github.com/mstksg/auto-examples++### Buzzwords explained!+ * **Haskell DSL/library**: It's a Haskell library that provides a domain-specific language for composing and declaring your programs/games. @@ -150,22 +180,6 @@ As it has been called by ertes, it's "save states for free". [spa]: http://www.haskellforall.com/2014/04/scalable-program-architectures.html--Intrigued? Excited? Start at [the tutorial][tutorial]!--[tutorial]: https://github.com/mstksg/auto/blob/master/tutorial/tutorial.md--It's a part of this package directory and also on github at the above link.-The current development documentation server is found at-<https://mstksg.github.io/auto>.--From there, you can check out my [All About Auto][aaa] series on my blog,-where I break sample projects and show to approach projects in real life. You-can also find examples and demonstrations in the [auto-examples][] repo on-github.--[aaa]: http://blog.jle.im/entries/series/+all-about-auto-[auto-examples]: https://github.com/mstksg/auto-examples ### Support
auto.cabal view
@@ -1,5 +1,5 @@ name: auto-version: 0.4.0.0+version: 0.4.1.0 synopsis: Denotative, locally stateful programming DSL & platform description: (Up to date documentation is maintained at <https://mstksg.github.com/auto>)@@ -39,7 +39,9 @@ Also, check out the <https://github.com/mstksg/auto-examples auto-examples> repository on github for plenty of real-world and toy- examples to learn from!+ examples to learn from; I've also done a+ <blog.jle.im/entries/series/+all-about-auto blog series>+ on this library, for examples and full tutorials! . Support available on freenode's #haskell-auto, #haskell-game, and also on the github issue
src/Control/Auto/Blip.hs view
@@ -271,13 +271,56 @@ -- | Merge all of the blip streams together, using the given merging -- function associating from the right.+--+-- __DEPRECATED__: In its current form, 'foldrB' will disappear in @0.5@.+-- The new version will be:+--+-- @+-- foldrB :: (a -> a -> a) -> [Blip a] -> Blip b+-- @+--+-- Which will not emit if nothing emits. This really was supposed to be+-- the intended behavior originally.+--+-- For this reason, please do not use this anymore. As it is currently+-- implemented, it doesn't really make any sense, either.+--+-- To begin using the new behavior, you can use:+--+-- @+-- foldr (merge f) mempty+-- @+-- foldrB :: (a -> a -> a) -> a -> [Blip a] -> Blip a-foldrB f b0 = foldr (merge f) (Blip b0)+foldrB f x0 = foldr (merge f) (Blip x0)+-- foldrB :: (a -> a -> a) -> [Blip a] -> Blip a+-- foldrB f = foldr (merge f) NoBlip -- | Merge all of the blip streams together, using the given merging -- function associating from the left.+--+-- __DEPRECATED__: In its current form, 'foldlB'' will disappear in @0.5@.+-- The new version will be:+--+-- @+-- foldlB' :: (a -> a -> a) -> [Blip a] -> Blip b+-- @+--+-- Which will not emit if nothing emits. This really was supposed to be+-- the intended behavior originally.+--+-- For this reason, please do not use this anymore. As it is currently+-- implemented, it doesn't really make any sense, either.+--+-- To begin using the new behavior, you can use:+--+-- @+-- foldl' (merge f) mempty+-- @ foldlB' :: (a -> a -> a) -> a -> [Blip a] -> Blip a-foldlB' f b0 = foldl' (merge f) (Blip b0)+foldlB' f x0 = foldl' (merge f) (Blip x0)+-- foldlB' :: (a -> a -> a) -> [Blip a] -> Blip a+-- foldlB' f = foldl' (merge f) NoBlip -- | Takes two 'Auto's producing blip streams and returns a "merged"@@ -337,11 +380,11 @@ -- prop> immediately == inB 1 inB :: Int -- ^ number of steps before value is emitted. -> Auto m a (Blip a)-inB n = mkState f (n, False)+inB = mkState f . Just where- f _ (_, True ) = (NoBlip, (1 , True ))- f x (i, False) | i <= 1 = (Blip x, (1 , True ))- | otherwise = (NoBlip, (i-1, False))+ f _ Nothing = (NoBlip, Nothing)+ f x (Just i) | i <= 1 = (Blip x, Nothing)+ | otherwise = (NoBlip, Just (i - 1)) -- | Produces a blip stream that emits the input value whenever the input -- satisfies a given predicate.@@ -636,14 +679,12 @@ onFlip p = became p &> noLonger p -- | The non-serializing/non-resumable version of 'became'.-became_ :: Monad m- => (a -> Bool) -- ^ change condition+became_ :: (a -> Bool) -- ^ change condition -> Auto m a (Blip a) became_ p = accum_ (_becameF p) NoBlip -- | The non-serializing/non-resumable version of 'noLonger'.-noLonger_ :: Monad m- => (a -> Bool) -- ^ change condition+noLonger_ :: (a -> Bool) -- ^ change condition -> Auto m a (Blip a) noLonger_ p = became_ (not . p) @@ -661,8 +702,7 @@ -- -- Useful because it can be serialized without the output needing -- a 'Serialize' instance.-became' :: Monad m- => (a -> Bool) -- ^ change condition+became' :: (a -> Bool) -- ^ change condition -> Auto m a (Blip ()) became' p = accum f NoBlip where@@ -674,12 +714,13 @@ -- -- Useful because it can be serialized without the output needing -- a 'Serialize' instance.-noLonger' :: Monad m- => (a -> Bool) -- ^ change condition+noLonger' :: (a -> Bool) -- ^ change condition -> Auto m a (Blip ()) noLonger' p = became' (not . p) --- | Like 'onFlip', but emits a '()' instead of the triggering input value.+-- | Like 'onFlip', but emits a 'Bool' instead of the triggering input+-- value. An emitted 'True' indicates that the predicate just became true;+-- an emitted 'False' indicates that the predicate just became false. -- -- Useful because it can be serialized without the output needing -- a 'Serialize' instance.
src/Control/Auto/Core.hs view
@@ -1822,7 +1822,6 @@ atanh = fmap atanh acosh = fmap acosh - -- Utility functions firstM :: Monad m => (a -> m b) -> (a, c) -> m (b, c)
src/Control/Auto/Effects.hs view
@@ -44,6 +44,9 @@ , sealReader , sealReader_ , readerA+ -- *** Sealing from sources+ , sealReaderMVar+ , sealReaderM -- ** 'WriterT' -- $writer@@ -70,8 +73,10 @@ import Control.Auto.Core import Control.Auto.Generate import Control.Category+import Control.Concurrent import Control.Exception import Control.Monad hiding (mapM, mapM_)+import Control.Monad.IO.Class import Control.Monad.Trans.Reader (ReaderT(ReaderT), runReaderT) import Control.Monad.Trans.State (StateT(StateT), runStateT) import Control.Monad.Trans.Writer (WriterT(WriterT), runWriterT)@@ -375,25 +380,80 @@ => Auto (ReaderT r m) a b -- ^ 'Auto' run over 'Reader' -> r -- ^ the perpetual environment -> Auto m a b-sealReader a r = mkAutoM (sealReader <$> resumeAuto a <*> get)- (saveAuto a *> put r)- $ \x -> do- (y, a') <- runReaderT (stepAuto a x) r- return (y, sealReader a' r)+sealReader a0 r = go a0+ where+ go a = mkAutoM (sealReader <$> resumeAuto a <*> get)+ (saveAuto a *> put r)+ $ \x -> do+ (y, a') <- runReaderT (stepAuto a x) r+ return (y, go a') -- | The non-resuming/non-serializing version of 'sealReader'. Does not -- serialize/reload the @r@ environment, so that whenever you "resume" the -- 'Auto', it uses the new @r@ given when you are trying to resume, instead -- of loading the originally given one.+--+-- /DOES/ serialize the actual 'Auto'! sealReader_ :: Monad m => Auto (ReaderT r m) a b -- ^ 'Auto' run over 'Reader' -> r -- ^ the perpetual environment -> Auto m a b-sealReader_ a r = mkAutoM (sealReader_ <$> resumeAuto a <*> pure r)- (saveAuto a)- $ \x -> do- (y, a') <- runReaderT (stepAuto a x) r- return (y, sealReader_ a' r)+sealReader_ a0 r = go a0+ where+ go a = mkAutoM (go <$> resumeAuto a)+ (saveAuto a)+ $ \x -> do+ (y, a') <- runReaderT (stepAuto a x) r+ return (y, go a')++-- | Takes an 'Auto' that operates under the context of a read-only+-- environment, an environment value, and turns it into a normal 'Auto'+-- that always gets its environment value by executing an action every step+-- in the underlying monad.+--+-- This can be abused to write unreadable code really fast if you don't use+-- it in a disciplined way. One possible usage is to query a database in+-- 'IO' (or 'MonadIO') for a value at every step. If you're using+-- underlying global state, you can use it to query that too, with 'get' or+-- 'gets'. You could even use 'getLine', maybe, to get the result from+-- standard input at every step.+--+-- One disciplined wrapper around this is 'sealReaderMVar', where the+-- environment at every step comes from reading an 'MVar'. This can be+-- used to "hot swap" configuration files.+--+sealReaderM :: Monad m+ => Auto (ReaderT r m) a b -- ^ 'Auto' run over 'Reader'+ -> m r -- ^ action to draw new @r@ at every step+ -> Auto m a b+sealReaderM a0 r' = go a0+ where+ go a = mkAutoM (go <$> resumeAuto a)+ (saveAuto a)+ $ \x -> do+ r <- r'+ (y, a') <- runReaderT (stepAuto a x) r+ return (y, go a')+++-- | Takes an 'Auto' that operates under the context of a read-only+-- environment, an environment value, and turns it into a normal 'Auto'+-- that always gets its environment value from an 'MVar'.+--+-- This allows for "hot swapping" configurations. If your whole program+-- runs under a configuration data structure as the environment, you can+-- load the configuration data to the 'MVar' and then "hot swap" it out by+-- just changing the value in the 'MVar' from a different thread.+--+-- Note that this will block on every "step" until the 'MVar' is+-- readable/full/has a value, if it does not.+--+-- Basically a disciplined wrapper/usage over 'sealReaderM'.+sealReaderMVar :: MonadIO m+ => Auto (ReaderT r m) a b -- ^ 'Auto' run over 'Reader'+ -> MVar r -- ^ 'MVar' containing an @r@ for every step+ -> Auto m a b+sealReaderMVar a0 mv = sealReaderM a0 $ liftIO (readMVar mv) -- | Transforms an 'Auto' on two input streams ( a "normal input" stream -- @a@ and an "environment input stream" @r@) into an 'Auto' on one input
src/Control/Auto/Interval.hs view
@@ -158,8 +158,8 @@ -- expect/. By saying that a function expects an 'Interval': -- -- @--- chooseInterval :: [Interval m a b]--- -> Interval m a b+-- chooseInterval :: [Interval m a b]+-- -> Interval m a b -- @ -- -- 'chooseInterval' has the ability to "state" that it /expects/ things@@ -513,22 +513,22 @@ holdFor :: Serialize a => Int -- ^ number of steps to hold the last emitted value for -> Interval m (Blip a) a-holdFor n = mkState (_holdForF n) (Nothing, max 0 n)+holdFor n = mkState (_holdForF (max 0 n)) (Nothing, Nothing) -- | The non-serializing/non-resuming version of 'holdFor'. holdFor_ :: Int -- ^ number of steps to hold the last emitted value for -> Interval m (Blip a) a-holdFor_ n = mkState_ (_holdForF n) (Nothing, max 0 n)+holdFor_ n = mkState_ (_holdForF (max 0 n)) (Nothing, Nothing) -_holdForF :: Int -> Blip a -> (Maybe a, Int) -> (Maybe a, (Maybe a, Int))+_holdForF :: Int -> Blip a -> (Maybe a, Maybe Int) -> (Maybe a, (Maybe a, Maybe Int)) _holdForF n = f -- n should be >= 0 where f x s = (y, (y, i)) where (y, i) = case (x, s) of- (Blip b, _ ) -> (Just b , n )- (_ , (_, 0)) -> (Nothing, 0 )- (_ , (z, j)) -> (z , j - 1)+ (Blip b, _ ) -> (Just b , Just n )+ (_ , (z, Just j )) | j > 1 -> (z, Just (j - 1))+ _ -> (Nothing, Nothing) -- | "Stretches" the last "on"/'Just' input over the entire range of -- following "off"/'Nothing' inputs. Holds on to the last 'Just' until@@ -673,7 +673,7 @@ -- during :: Monad m => Auto m a b -- ^ 'Auto' to lift to work over intervals- -> Auto m (Maybe a) (Maybe b)+ -> Interval m (Maybe a) b during = dimap to from . right where from = either (const Nothing) Just
src/Control/Auto/Run.hs view
@@ -21,12 +21,16 @@ -- module Control.Auto.Run (+ -- * Lifting inputs and outputs+ throughT -- * Special 'stepAuto' versions. -- ** Streaming over lists- streamAuto+ , streamAuto , streamAuto' , overList , overList'+ , overTraversable+ , overTraversable' -- ** Running over one item repetitively , stepAutoN , stepAutoN'@@ -51,16 +55,19 @@ ) where import Control.Applicative+import Control.Arrow import Control.Auto.Core import Control.Auto.Interval import Control.Concurrent-import Control.Monad hiding (mapM, mapM_)+import Control.Monad hiding (mapM, mapM_) import Control.Monad.Trans.Class+import Control.Monad.Trans.State.Strict import Data.Functor.Identity import Data.Maybe import Data.Profunctor-import Prelude hiding (interact, mapM, mapM_)-import Text.Read hiding (lift)+import Data.Traversable (Traversable(..))+import Prelude hiding (interact, mapM, mapM_)+import Text.Read hiding (lift, get) -- | 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@@ -107,6 +114,21 @@ (ys, a'') <- overList a' xs return (y:ys, a'') +-- | Like 'overList', but "streams" the 'Auto' over all elements of any+-- 'Traversable', returning the final updated 'Auto'.+overTraversable :: (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'+overTraversable a0 = flip runStateT a0 . mapM f+ where+ f x = do+ a <- get+ (y, a') <- lift $ stepAuto a x+ put a'+ return y++ -- | 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''.@@ -129,6 +151,19 @@ (ys, a'') = overList' a' xs in (y:ys, a'') +-- | Like 'overList'', but "streams" the 'Auto'' over all elements of any+-- 'Traversable'', returning the final updated 'Auto''.+overTraversable' :: 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''+overTraversable' a0 = flip runState a0 . mapM f+ where+ f x = do+ a <- get+ let (y, a') = stepAuto' a x+ y <$ put a'+ -- | 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@@ -335,6 +370,38 @@ -- -> a -- -> Auto' a b -- execAutoN' n a0 = snd . stepAutoN' n a0++-- | 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 'Auto's lifted and all lawful 'Traversable's, usage should+-- obey the laws:+--+-- @+-- throughT id = id+-- throughT g . throughT f = throughT (g . f)+-- @+throughT :: (Monad m, Traversable t)+ => Auto m a b -- ^ 'Auto' to run "through" a 'Traversable'+ -> Auto m (t a) (t b)+throughT a = mkAutoM (throughT <$> resumeAuto a)+ (saveAuto a)+ (liftM (second throughT) . overTraversable a) -- | Heavy duty abstraction for "self running" an 'Auto'. Give a starting -- input action, a (possibly side-effecting) function from an output to
src/Control/Auto/Time.hs view
@@ -75,7 +75,7 @@ import Control.Category import Control.Monad import Control.Monad.Trans.Class-import Control.Monad.Trans.Writer+import Control.Monad.Trans.Writer.Strict import Data.Maybe import Data.Monoid import Data.Serialize
tutorial/tutorial.md view
@@ -371,7 +371,7 @@ ~~~haskell ghci> streamAuto' foo [4,7,3,6,5,1]-[ ( 4, Just 4), ( 144, Just 4), ( 2448, Just 4)+[ ( 8, Just 4), ( 144, Just 4), ( 2448, Just 4) , (63648, Just 6), (1909440, Just 6), (51554880, Just 6) ] ~~~