auto 0.4.1.0 → 0.4.2.0
raw patch · 9 files changed
+320/−42 lines, 9 filesdep ~MonadRandomdep ~basedep ~bytestringPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: MonadRandom, base, bytestring, cereal, containers, deepseq, profunctors, random, semigroups, transformers
API changes (from Hackage documentation)
+ Control.Auto.Blip: asMaybes :: Auto m (Blip a) (Maybe a)
+ Control.Auto.Blip: collectB :: (Serialize a, Serialize b) => Auto m (Blip a, Blip b) (Blip (a, b))
+ Control.Auto.Blip: collectB_ :: Auto m (Blip a, Blip b) (Blip (a, b))
+ Control.Auto.Blip: collectBs :: Serialize a => Int -> Auto m (Blip a) (Blip [a])
+ Control.Auto.Blip: collectBs_ :: Int -> Auto m (Blip a) (Blip [a])
+ Control.Auto.Blip: collectN :: Serialize a => Int -> Auto m a (Blip [a])
+ Control.Auto.Blip: collectN_ :: Int -> Auto m a (Blip [a])
+ Control.Auto.Blip: emitEithers :: (a -> Either b c) -> Auto m a (Blip b, Blip c)
+ Control.Auto.Blip: onEithers :: Auto m (Either a b) (Blip a, Blip b)
+ Control.Auto.Blip: substituteB :: Auto m (a, Blip a) a
+ Control.Auto.Collection: muxI :: (Serialize k, Ord k, Monad m) => (k -> Interval m a b) -> Auto m (k, a) (Maybe b)
+ Control.Auto.Collection: muxI_ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (k, a) (Maybe b)
+ Control.Auto.Collection: muxManyI :: (Serialize k, Ord k, Monad m) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
+ Control.Auto.Collection: muxManyI_ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
+ Control.Auto.Switch: switchIn :: Monad m => Int -> Auto m a b -> Auto m a b -> Auto m a b
Files
- CHANGELOG.md +41/−4
- auto.cabal +12/−12
- src/Control/Auto/Blip.hs +112/−4
- src/Control/Auto/Collection.hs +94/−14
- src/Control/Auto/Core.hs +16/−3
- src/Control/Auto/Effects.hs +6/−1
- src/Control/Auto/Interval.hs +2/−1
- src/Control/Auto/Switch.hs +36/−1
- src/Control/Auto/Time.hs +1/−2
CHANGELOG.md view
@@ -1,3 +1,40 @@+0.4.2.0+-------+<https://github.com/mstksg/auto/releases/tag/v0.4.2.0>++* Removed all upper bounds on dependencies.+* **Control.Auto.Blip**: Companions to `emitJusts` and `onJusts` added, for+ `Either`: `emitEithers` and `onEithers`. Emit every item inputted, but+ fork them into one of two output blit streams based on `Right` or `Left`+ properties. Only preserves blip semantics/makes sense if any given+ input's `Right` or `Left`ness is expected to be independent from the last+ received one.+* **Control.Auto.Blip**: New "blip stream collapsers", `asMaybes` and+ `substituteB`. `asMaybes` turns a blip stream into a stream of `Maybe`s,+ `Just` when something was emitted, and `Nothing` when not. `substituteB`+ takes a regular stream and a blip stream, and outputs the values of the+ regular stream whenever the blip stream doesn't emit and the emitted value+ when it does --- basically a more powerful version of `fromBlips`, where+ the "default" value now comes from a stream instead of being always the+ same.+* **Control.Auto.Blip**: New blip stream creator, `collectN`, which emits+ every `n` steps with the last `n` items received.+* **Control.Auto.Blip**: New blip stream modifiers, `collectB` and+ `collectBs`. `collectB` waits on two blip streams and emits after it has+ received something from *both*. `collectBs` is like `collectN`, except+ emits after every `n` emitted values received with the past `n` emitted+ values.+* **Control.Auto.Collection**: "Intervaled" counterparts to `mux` and+ `muxMany`, `muxI` and `muxManyI`. They store `Interval`s instead of+ `Auto`s...and when the `Interval`s turned "off", they are removed from the+ collection.+* **Control.Auto.Switch**: A new "count-down" swithcer, `switchIn`, which+ acts a bit like `(-->)` and `(-?>)`, except the switch happens+ deterministically after a pre-set given number of steps. Act like the+ first `Auto` for a given number of steps, and then act like the second+ ever after. Basically a direct implementation of the common+ `onFor n a1 --> a2` idiom.+ 0.4.1.0 ------- <https://github.com/mstksg/auto/releases/tag/v0.4.1.0>@@ -5,10 +42,10 @@ * 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:+ their current forms. From version `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
auto.cabal view
@@ -1,5 +1,5 @@ name: auto-version: 0.4.1.0+version: 0.4.2.0 synopsis: Denotative, locally stateful programming DSL & platform description: (Up to date documentation is maintained at <https://mstksg.github.com/auto>)@@ -41,7 +41,7 @@ repository on github for plenty of real-world and toy 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!+ on this library, with examples and full tutorials! . Support available on freenode's #haskell-auto, #haskell-game, and also on the github issue@@ -85,16 +85,16 @@ , Control.Auto.Time -- other-modules: -- other-extensions:- build-depends: base >= 4.6 && < 4.9- , MonadRandom >= 0.3.0.1 && < 0.4- , bytestring >= 0.10.4.0 && < 0.11- , cereal >= 0.4.1.1 && < 0.5- , containers >= 0.5.5.1 && < 0.6- , deepseq >= 1.3.0.2 && < 2.0- , profunctors >= 4.3 && < 5.0- , random >= 1.1 && < 2.0- , semigroups >= 0.16 && < 0.17- , transformers >= 0.4.2.0 && < 0.5+ build-depends: base >= 4.6 && < 5+ , MonadRandom >= 0.3.0.1+ , bytestring >= 0.10.4.0+ , cereal >= 0.4.1.1+ , containers >= 0.5.5.1+ , deepseq >= 1.3.0.2+ , profunctors >= 4.3+ , random >= 1.1+ , semigroups >= 0.16+ , transformers >= 0.4.2.0 hs-source-dirs: src default-language: Haskell2010 ghc-options: -Wall
src/Control/Auto/Blip.hs view
@@ -31,15 +31,19 @@ , mergeRs , foldrB , foldlB'- -- ** Blip stream creation (dangerous!)+ -- ** "Raw" blip stream creation (dangerous!) , emitJusts- , emitOn+ , emitEithers , onJusts+ , onEithers+ , emitOn -- ** Blip stream collapse , fromBlips , fromBlipsWith+ , asMaybes , holdWith , holdWith_+ , substituteB -- * Step/"time" based Blip streams and generators , never , immediately@@ -47,6 +51,8 @@ , every , eachAt , eachAt_+ , collectN+ , collectN_ -- * Modifying Blip streams , tagBlips , modifyBlips@@ -58,6 +64,10 @@ , lagBlips_ , filterB , splitB+ , collectB+ , collectB_+ , collectBs+ , collectBs_ , joinB , mapMaybeB , takeB@@ -293,6 +303,9 @@ -- foldrB :: (a -> a -> a) -> a -> [Blip a] -> Blip a foldrB f x0 = foldr (merge f) (Blip x0)+{-# DEPRECATED foldrB "Starting in v0.5, will have new functionality." #-}++ -- foldrB :: (a -> a -> a) -> [Blip a] -> Blip a -- foldrB f = foldr (merge f) NoBlip @@ -319,6 +332,8 @@ -- @ foldlB' :: (a -> a -> a) -> a -> [Blip a] -> Blip a foldlB' f x0 = foldl' (merge f) (Blip x0)+{-# DEPRECATED foldlB' "Starting in v0.5, will have new functionality." #-}+ -- foldlB' :: (a -> a -> a) -> [Blip a] -> Blip a -- foldlB' f = foldl' (merge f) NoBlip @@ -386,6 +401,38 @@ f x (Just i) | i <= 1 = (Blip x, Nothing) | otherwise = (NoBlip, Just (i - 1)) +-- | @'collectN' n@ emits every @n@ steps, emitting with the @n@ last items+-- received.+--+-- >>> streamAuto' (collectN 3) [1..10]+-- [ NoBlip, NoBlip, Blip [1,2,3], NoBlip, NoBlip, Blip [4,5,6]+-- , NoBlip, NoBlip, Blip [7,8,9], NoBlip ]+--+collectN :: Serialize a => Int -> Auto m a (Blip [a])+collectN (max 1 -> n) = mkState (_collectBsF n . Blip) (n, [])++-- | The non-serializing/non-resuming version of 'collectN'+collectN_ :: Int -> Auto m a (Blip [a])+collectN_ (max 1 -> n) = mkState_ (_collectBsF n . Blip) (n, [])++-- | A blip stream that listens to an input blip stream and emits after the+-- input stream emits a given number of times. Emits with a list of all+-- received emitted values.+collectBs :: Serialize a => Int -> Auto m (Blip a) (Blip [a])+collectBs (max 1 -> n) = mkState (_collectBsF n) (n, [])++-- | The non-serializing/non-resuming version of 'collectBs'.+collectBs_ :: Int -> Auto m (Blip a) (Blip [a])+collectBs_ (max 1 -> n) = mkState_ (_collectBsF n) (n, [])++-- n expected to be strictly positive+_collectBsF :: Int -> Blip a -> (Int, [a]) -> (Blip [a], (Int, [a]))+_collectBsF n = f+ where+ f (Blip x) (i, xs) | i == 1 = (Blip (reverse (x:xs)), (n , [] ))+ | otherwise = (NoBlip , (i - 1, x:xs))+ f _ s = (NoBlip, s)+ -- | Produces a blip stream that emits the input value whenever the input -- satisfies a given predicate. --@@ -507,6 +554,34 @@ joinB :: Auto m (Blip (Blip a)) (Blip a) joinB = mkFunc (blip NoBlip id) +-- | Waits on two streams, and emits with the first seen items when both+-- have emitted. Once it emits, starts over.+--+-- >>> streamAuto' collectB [(Blip 1, NoBlip), (Blip 2, Blip 'a'),(Blip 3, Blip 'b')]+-- [NoBlip, Blip (1, 'a'), Blip (3, 'b')]+--+-- Can be used to implement a sort of "parallel wait".+--+collectB :: (Serialize a, Serialize b)+ => Auto m (Blip a, Blip b) (Blip (a, b))+collectB = mkState _collectBF (Nothing, Nothing)++-- | The non-serializing/non-resuming version of 'collectB'.+collectB_ :: Auto m (Blip a, Blip b) (Blip (a, b))+collectB_ = mkState_ _collectBF (Nothing, Nothing)++_collectBF :: (Blip a, Blip b)+ -> (Maybe a, Maybe b)+ -> (Blip (a, b), (Maybe a, Maybe b))+_collectBF (b1, b2) (st1, st2) =+ case liftA2 (,) st1' st2' of+ Just (x, y) -> (Blip (x, y), (Nothing, Nothing))+ Nothing -> (NoBlip , (st1' , st2' ))+ where+ st1' = st1 <|> blip Nothing Just b1+ st2' = st2 <|> blip Nothing Just b2++ -- | Applies the given function to every emitted value, and suppresses all -- those for which the result is 'Nothing'. Otherwise, lets it pass -- through with the value in the 'Just'.@@ -756,16 +831,40 @@ -- semantics, we have "Control.Auto.Interval". -- -- See the examples of 'emitOn' for more concrete good/bad use cases.+--+-- prop> onJusts == emitJusts id onJusts :: Auto m (Maybe a) (Blip a) onJusts = mkFunc (maybe NoBlip Blip) +-- | Like 'onJusts', except forks into two streams depending on if the+-- input is 'Left' or 'Right'.+--+-- Is only meaningful if you expect every 'Left'/'Right' choice to be+-- independent of the last.+--+-- prop> onEithers == emitEithers id+onEithers :: Auto m (Either a b) (Blip a, Blip b)+onEithers = mkFunc $ \ex -> case ex of+ Left x -> (Blip x, NoBlip)+ Right x -> (NoBlip, Blip x)++-- | Like 'emitJusts', except forks into two streams depending on the+-- function's result being 'Left' or 'Right'.+--+-- Is only meaningful if you expect every 'Left'/'Right' choice to be+-- independent of the last.+emitEithers :: (a -> Either b c) -> Auto m a (Blip b, Blip c)+emitEithers f = mkFunc $ \x -> case f x of+ Left y -> (Blip y, NoBlip)+ Right y -> (NoBlip, Blip y)+ -- | @'fromBlips' d@ is an 'Auto' that decomposes the incoming blip -- stream by constantly outputting @d@ except when the stream emits, and -- outputs the emitted value when it does. fromBlips :: a -- ^ the "default value" to output when the input is not -- emitting. -> Auto m (Blip a) a-fromBlips d = mkFunc (blip d id)+fromBlips d = mkFunc $ blip d id -- | @'fromBlipsWith' d f@ is an 'Auto' that decomposes the incoming blip -- stream by constantly outputting @d@ except when the stream emits, and@@ -775,8 +874,17 @@ -> (a -> b) -- ^ the function to apply to the emitted value -- whenever input is emitting. -> Auto m (Blip a) b-fromBlipsWith d f = mkFunc (blip d f)+fromBlipsWith d f = mkFunc $ blip d f +-- | Collapse a blip stream of `a`s into a stream of `Maybe a`'s+asMaybes :: Auto m (Blip a) (Maybe a)+asMaybes = mkFunc $ blip Nothing Just++-- | Take in a normal stream and a blip stream. Behave like the normal+-- stream when the blip stream doesn't emit...but when it does, output the+-- emitted value instead.+substituteB :: Auto m (a, Blip a) a+substituteB = mkFunc $ \(x, b) -> blip x id b -- | @'holdWith' y0@ is an 'Auto' whose output is always the /most recently -- emitted/ value from the input blip stream. Before anything is emitted,
src/Control/Auto/Collection.hs view
@@ -61,9 +61,13 @@ -- ** Single input, single output , mux , mux_+ , muxI+ , muxI_ -- ** Multiple input, multiple output , muxMany , muxMany_+ , muxManyI+ , muxManyI_ -- * "Gathering"/accumulating collections -- ** Single input, multiple output , gather@@ -351,10 +355,6 @@ -- Whenever any of the internal 'Auto's return 'Nothing', they are removed -- from the collection. ----- Toy examples here are of limited use, but let's try it out. Here we--- will have a 'dynMap_' that feeds each internal 'Auto' back to itself.--- The result of each is sent directly back to itself.--- -- >>> import qualified Data.IntMap as IM -- >>> let dm0 :: Auto' (IM.IntMap Int) (IM.IntMap Int) -- dm0 = proc x -> do@@ -530,8 +530,8 @@ -- -- See 'mux' for more notes. muxMany :: (Serialize k, Ord k, Monad m)- => (k -> Auto m a b) -- ^ function to create a new 'Auto' if- -- none at that key already exists+ => (k -> Auto m a b) -- ^ function to create a new 'Auto' if+ -- none at that key already exists -> Auto m (Map k a) (Map k b) muxMany f = go mempty where@@ -545,21 +545,21 @@ t = _muxManyF f go -- | The non-serializing/non-resuming version of 'muxMany'.-muxMany_ :: forall m a b k. (Ord k, Monad m)- => (k -> Auto m a b) -- ^ function to create a new 'Auto' if- -- none at that key already exists+muxMany_ :: (Ord k, Monad m)+ => (k -> Auto m a b) -- ^ function to create a new 'Auto' if+ -- none at that key already exists -> Auto m (Map k a) (Map k b) muxMany_ f = go mempty where- go :: Map k (Auto m a b) -> Auto m (Map k a) (Map k b)+ -- go :: Map k (Auto m a b) -> Auto m (Map k a) (Map k b) go = mkAutoM_ . _muxManyF f go _muxManyF :: forall k m a b. (Ord k, Monad m)- => (k -> Auto m a b) -- ^ f : make new Autos+ => (k -> Auto m a b) -- ^ f : make new Autos -> (Map k (Auto m a b) -> Auto m (Map k a) (Map k b)) -- ^ go: make next step- -> Map k (Auto m a b) -- ^ as: all current Autos- -> Map k a -- ^ xs: Inputs- -> m (Map k b, Auto m (Map k a) (Map k b)) -- ^ Outputs, and next Auto.+ -> Map k (Auto m a b) -- ^ as: all current Autos+ -> Map k a -- ^ xs: Inputs+ -> m (Map k b, Auto m (Map k a) (Map k b)) -- ^ Outputs, and next Auto. _muxManyF f go as xs = do -- all the outputs of the autos with the present inputs; autos without -- inputs are ignored.@@ -578,6 +578,86 @@ -- corresponding input. steps :: Map k (m (b, Auto m a b)) steps = M.intersectionWith stepAuto allas xs+++-- | Like 'muxI', but holds 'Interval's instead. When any given 'Interval'+-- turns "off", it's removed from the collection. If its key is fed in+-- again, it'll be restarted with the initializing function. On the actual+-- step when it turns "off", 'Nothing' will be returned.+muxI :: (Serialize k, Ord k, Monad m)+ => (k -> Interval m a b) -- ^ function to create a new 'Auto' if none at+ -- that key already exists.+ -> Auto m (k, a) (Maybe b)+muxI f = dimap (uncurry M.singleton) (listToMaybe . M.elems) (muxManyI f)++-- | The non-serializing/non-resuming version of 'muxI'.+muxI_ :: (Ord k, Monad m)+ => (k -> Interval m a b) -- ^ function to create a new 'Auto' if none at+ -- that key already exists+ -> Auto m (k, a) (Maybe b)+muxI_ f = dimap (uncurry M.singleton) (listToMaybe . M.elems) (muxManyI_ f)++-- | Like 'muxManyI', but holds 'Interval's instead. When any given+-- 'Interval' turns "off", it's removed from the collection. Only+-- 'Interval's that are "on" after the step will be present in the output+-- 'Map'.+muxManyI :: (Serialize k, Ord k, Monad m)+ => (k -> Interval m a b) -- ^ function to create a new 'Auto' if+ -- none at that key already exists+ -> Auto m (Map k a) (Map k b)+muxManyI f = go mempty+ where+ -- go :: Map k (Interval m a b) -> Auto m (Map k a) (Map k b)+ go as = mkAutoM l (s as) (t as)+ l = do+ ks <- get+ let as = M.fromList (map (id &&& f) ks)+ go <$> mapM resumeAuto as+ s as = put (M.keys as) *> mapM_ saveAuto as+ t = _muxManyIF f go++-- | The non-serializing/non-resuming version of 'muxManyI'.+muxManyI_ :: (Ord k, Monad m)+ => (k -> Interval m a b) -- ^ function to create a new 'Auto' if+ -- none at that key already exists+ -> Auto m (Map k a) (Map k b)+muxManyI_ f = go mempty+ where+ -- go :: Map k (Interval m a b) -> Auto m (Map k a) (Map k b)+ go = mkAutoM_ . _muxManyIF f go++_muxManyIF :: forall k m a b. (Ord k, Monad m)+ => (k -> Interval m a b) -- ^ f : make new Autos+ -> (Map k (Interval m a b) -> Auto m (Map k a) (Map k b)) -- ^ go: make next step+ -> Map k (Interval m a b) -- ^ as: all current Autos+ -> Map k a -- ^ xs: Inputs+ -> m (Map k b, Auto m (Map k a) (Map k b)) -- ^ Outputs, and next Auto.+_muxManyIF f go as xs = do+ -- all the outputs of the autos with the present inputs; autos without+ -- inputs are ignored.+ outs <- sequence steps+ let outs' = M.mapMaybe filterDead outs -- Nothings removed+ ys = fmap fst outs'+ allas' = M.union (fmap snd outs') leftOuts+ return (ys, go allas')+ where+ -- new Autos, from the function. Only on new ones not found in `as`.+ newas :: Map k (Interval m a b)+ newas = M.mapWithKey (\k _ -> f k) (M.difference xs as)+ -- all Autos, new and old. Prefer the old ones.+ allas :: Map k (Interval m a b)+ allas = M.union as newas+ -- Step all the autos with all the inputs. Lose the Autos that have no+ -- corresponding input.+ steps :: Map k (m (Maybe b, Interval m a b))+ steps = M.intersectionWith stepAuto allas xs+ -- Autos not being stepped+ leftOuts :: Map k (Interval m a b)+ leftOuts = M.difference allas steps+ -- Get out the result if Just, otherwise erase it all.+ filterDead :: (Maybe b, Interval m a b) -> Maybe (b, Interval m a b)+ filterDead (Just x, i) = Just (x, i)+ filterDead _ = Nothing e2m :: Either (a, b) b -> (Maybe a, b) e2m (Left (x, y)) = (Just x , y)
src/Control/Auto/Core.hs view
@@ -827,7 +827,7 @@ -- Nothing -> do -- (y, a2') <- stepAuto a2 x -- return (y, switched a2')--- switched a = mkAutoM (switched <$> resumeAuto a)+-- switched a = mkAutoM l -- (put True *> saveAuto a) -- $ \x -> do -- (y, a') <- stepAuto a x@@ -1172,6 +1172,19 @@ -- -- (Compare with the example in 'accum') --+-- Note that this is more or less an encoding of 'scanl', that can be+-- "decoded" with 'streamAuto'':+--+-- >>> let myScanl f z = streamAuto' (accumD f z)+-- >>> scanl (+) 0 [1..10]+-- [0,3,6,10,15,21,28,36,45,55]+-- >>> myScanl (+) 0 [1..10]+-- [0,3,6,10,15,21,28,36,45]+--+-- The only difference is that you don't get the last element. (You could+-- force it out, if you wanted, by feeding any nonsense value in --- even+-- 'undefined'! --- and getting the result)+-- accumD :: Serialize b => (b -> a -> b) -- ^ accumulating function -> b -- ^ initial accumulator@@ -1308,8 +1321,8 @@ -- | When the underlying 'Monad'/'Applicative' @m@ is an 'Alternative', -- fork the input through each one and "squish" their results together--- inside the 'Alternative' context. Somewhat rarely used, because who--- uses an 'Alternative' @m@?+-- inside the 'Alternative' context. See 'runTraversableA' for similar use+-- cases. -- -- >>> streamAuto (arrM (mfilter even . Just)) [1..10] -- Nothing
src/Control/Auto/Effects.hs view
@@ -898,11 +898,15 @@ -- bar = arrM Just -- @ --+-- >>> streamAuto foo [2,4,6,7]+-- Nothing+-- >>> streamAuto' (runTraversableA foo) [2,4,6,7]+-- [Just 1, Just 2, Just 3, Nothing] -- >>> streamAuto (foo &&& bar) [2,4,6] -- Just [(1, 2),(2, 4),(3, 6)] -- >>> streamAuto (foo &&& bar) [2,4,6,7] -- Nothing--- >>> streamAuto' ('runTraversableA' foo '<|?>' 'runTraversableA' bar) [2,4,6,7]+-- >>> streamAuto' (runTraversableA foo <|?> runTraversableA bar) [2,4,6,7] -- [Just 1, Just 2, Just 3, Just 7] runTraversableA :: (Monad f, Traversable f) => Auto f a b -- ^ 'Auto' run over traversable structure@@ -915,6 +919,7 @@ y = liftM fst o a' = liftM snd o in (y, go a')+ -- | Wraps a "try" over an underlying 'IO' monad; if the Auto encounters a -- runtime exception while trying to "step" itself, it'll output a 'Left'
src/Control/Auto/Interval.hs view
@@ -243,7 +243,7 @@ -- 'bindI' i2 . i1 :: 'Interval' m a b c -- @ ----- >>> let a1 = when (< 5) `compI` offFor 2+-- >>> let a1 = whenI (< 5) `compI` offFor 2 -- >>> streamAuto' a1 [1..6] -- [Nothing, Nothing, Just 3, Just 4, Nothing, Nothing] --@@ -743,3 +743,4 @@ -> Interval m a b -- ^ ...to this one -> Interval m a c compI f g = fmap join (during f) . g+
src/Control/Auto/Switch.hs view
@@ -33,6 +33,7 @@ -- * Sequential switching (-->) , (-?>)+ , switchIn -- * Arbitrary switching , switchFrom_ , switchOn_@@ -153,12 +154,46 @@ Nothing -> do (y, a2') <- stepAuto a2 x return (y, switched a2')- switched a = mkAutoM (switched <$> resumeAuto a)+ switched a = mkAutoM l (put True *> saveAuto a) $ \x -> do (y, a') <- stepAuto a x return (y, switched a') -- TODO: Add tests for the serialization here.++-- | @'switchIn' n a1 a2@ will behave like @a1@ for @n@ steps of output,+-- and then behave like @a2@ forever after.+--+-- More or less a more efficient/direct implementation of the common idiom:+--+-- @+-- onFor n a1 --> a2+-- @+--+--+switchIn :: Monad m+ => Int -- ^ number of outputs before switching+ -> Auto m a b -- ^ initial behavior+ -> Auto m a b -- ^ switched behavior+ -> Auto m a b+switchIn n a1 a2 | n > 0 = mkAutoM l s t+ | otherwise = switched a2+ where+ l = do+ flag <- get+ if flag+ then resumeAuto (switched a2)+ else switchIn <$> get <*> resumeAuto a1 <*> pure a2+ s = put False *> put n *> saveAuto a1+ t x = do+ (y, a1') <- stepAuto a1 x+ return (y, switchIn (n - 1) a1' a2)+ switched a = mkAutoM l+ (put True *> saveAuto a)+ $ \x -> do+ (y, a') <- stepAuto a x+ return (y, switched a')+ -- | Takes an 'Auto' who has both a normal output stream and a blip stream -- output stream, where the blip stream emits new 'Auto's.
src/Control/Auto/Time.hs view
@@ -36,6 +36,7 @@ count , count_ -- * Manipulating time+ , priming -- ** Delaying , lastVal , lastVal_@@ -47,8 +48,6 @@ , delayList_ , delayN , delayN_- -- ** "Priming"- , priming -- ** Stretching , stretch , stretch_