packages feed

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 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) ] ~~~