-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Denotative, locally stateful programming DSL & platform -- @package auto @version 0.4.2.2 -- | This module exposes an "unsafe" interface for working with the -- internal representation of "blip streams". If you are programming at -- the logic level or the application level, you should thoroughly be -- able to avoid importing this, and should be happy with importing the -- Blip type from Control.Auto and blip stream manipulators -- from Control.Auto.Blip. -- -- If, however, you are programming a framework, library, or backend, you -- might find it useful to manually create your own blip streams/sources. -- In this case, this module will be useful. -- -- It is important, as with most of this library in general, to always -- keep in mind when you are programming at the "logic" level, and when -- you are programming at the "backend" level. If you can justify that -- you are at the backend level and not at the logic level of whatever -- you are programming, then this is useful. -- -- Be sure, of course, that whatever blip streams you do manually -- construct and export preserve "Blip semantics", which is further -- defined in Control.Auto.Blip. -- -- You have been warned! module Control.Auto.Blip.Internal -- | When used in the context of an input or output of an Auto, a -- Blip a represents a stream that occasionally, at -- "independent" or "discrete" points, emits a value of type a. -- -- Contrast this to Interval, where things are meant to be "on" -- or "off" for contiguous chunks at a time; blip streams are "blippy", -- and Intervals are "chunky". -- -- It's here mainly because it's a pretty useful abstraction in the -- context of the many combinators found in various modules of this -- library. If you think of an Auto m a (Blip b) -- as producing a "blip stream", then there are various combinators and -- functions that are specifically designed to manipulate blip streams. -- -- For the purposes of the semantics of what Blip is supposed to -- represent, its constructors are hidden. (Almost) all of the various -- Blip combinators (and its very useful Functor instance) -- "preserve Blipness" --- one-at-a-time occurrences remain -- one-at-a-time under all of these combinators, and you should have -- enough so that direct access to the constructor is not needed. -- -- If you are creating a framework, library, or backend, you might want -- to manually create blip stream-producing Autos for your users -- to access. In this case, you can import the constructors and useful -- internal (and, of course, semantically unsafe) functions from -- Control.Auto.Blip.Internal. data Blip a NoBlip :: Blip a Blip :: !a -> Blip a -- | Merge two blip streams together; the result emits with either -- of the two merged streams emit. When both emit at the same time, emit -- the result of applying the given function on the two emitted values. -- -- Note that this might be too strict for some purposes; see -- mergeL and mergeR for lazier alternatives. merge :: (a -> a -> a) -> Blip a -> Blip a -> Blip a -- | Slightly more powerful merge, but I can't imagine a situation -- where this power is necessary. -- -- If only the first stream emits, emit with the first function applied -- to the value. If only the second stream emits, emit with the second -- function applied to the value. If both emit, then emit with the third -- function applied to both emitted values. merge' :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Blip a -> Blip b -> Blip c -- | Merges two blip streams together into one, which emits either -- of the original blip streams emit. If both emit at the same time, the -- left (first) one is favored. -- -- Lazy on the second stream if the first stream is emitting. -- -- If we discount laziness, this is merge const. mergeL :: Blip a -> Blip a -> Blip a -- | Merges two blip streams together into one, which emits either -- of the original blip streams emit. If both emit at the same time, the -- right (second) one is favored. -- -- Lazy on the first stream if the second stream is emitting. -- -- If we discount laziness, this is merge (flip -- const). mergeR :: Blip a -> Blip a -> Blip a -- | Deconstruct a Blip by giving a default result if the -- Blip is non-occuring and a function to apply on the contents, -- if the Blip is occuring. -- -- Try not to use if possible, unless you are a framework developer. If -- you're just making an application, try to use the other various -- combinators in this library. It'll help you preserve the semantics of -- what it means to be Blippy. -- -- Analogous to maybe from Prelude. blip :: b -> (a -> b) -> Blip a -> b instance Typeable Blip instance Functor Blip instance Show a => Show (Blip a) instance Generic (Blip a) instance Datatype D1Blip instance Constructor C1_0Blip instance Constructor C1_1Blip instance NFData a => NFData (Blip a) instance Serialize a => Serialize (Blip a) instance Semigroup a => Monoid (Blip a) instance Semigroup a => Semigroup (Blip a) -- | This module defines and provides the core types, (smart) constructors, -- and general high and low-level utilities used by the auto -- library. -- -- Note that importing and using functions from this module in part voids -- some of the "semantic contracts" of the Auto types you get, so -- use with caution! -- -- A lot of low-level functionality is provided here which is most likely -- unnecessary for most applications; many are mostly for internal usage -- or advanced/fine-grained usage. It also isn't really enough to do too -- many useful things, either. It's recommended that you import -- Control.Auto instead, which re-organizes the more useful parts -- of this module in addition with useful parts of others to provide a -- nice packaged entry point. If something in here becomes useful for -- more than just fine-tuning or low-level tweaking, it is probably -- supposed to be in Control.Auto anyway. -- -- Information on how to use these types is available in the -- tutorial! module Control.Auto.Core -- | The Auto type. For this library, an Auto semantically -- representsdenotes a a relationship/ between an input and an -- output that is preserved over multiple steps, where that relationship -- is (optionally) maintained within the context of a monad. -- -- A lot of fancy words, I know...but you can think of an Auto as -- nothing more than a "stream transformer" of value streams. A stream of -- sequential input values come in one at a time, and a stream of outputs -- pop out one at a time, as well. -- -- Using the streamAuto function, you can "unwrap" the inner -- value stream transformer from any Auto: if a :: Auto -- m a b, streamAuto lets you turn it into an [a] -> -- m [b]. "Give me a stream of as, one at a time, and I'll -- give you a list of bs, matching a relationship to your stream -- of as." -- --
-- -- unwrap your inner [a] -> m [b]! -- streamAuto :: Monad m => Auto m a b -> ([a] -> m [b]) ---- -- You can also turn an Auto m a b into an effects -- stream that executes effects sequentially with -- toEffectStream and streamAutoEffects, so you can run -- it with a ListT-compatible library like pipes. -- -- There's a handy type synonym Auto' for relationships that don't -- really need a monadic context; the m is just Identity: -- --
-- type Auto' = Auto Identity ---- -- So if you had an a :: Auto' a b, you can use -- streamAuto' to "unwrap" the inner stream transformer, [a] -- -> [b]. -- --
-- -- unwrap your inner [a] -> [b]! -- streamAuto' :: Auto' a b -> ([a] -> [b]) ---- -- All of the Autos given in this library maintain some sort of -- semantic relationship between streams --- for some, the outputs might -- be the inputs with a function applied; for others, the outputs might -- be the cumulative sum of the inputs. -- -- See the tutorial for more information! -- -- Operationally, an Auto m a b is implemented as a -- "stateful function". A function from an a where, every time -- you "apply" it, you get a b and an "updated -- Auto"/function with updated state. -- -- You can get this function using stepAuto: -- --
-- stepAuto :: Auto m a b -> (a -> m (b, Auto m a b)) ---- -- Or, for Auto', stepAuto': -- --
-- stepAuto' :: Auto' a b -> (a -> (b, Auto' a b)) ---- -- "Give me an a and I'll give you a b and your -- "updated" Auto". -- -- Autos really are mostly useful because they can be composed, -- chained, and modified using their various typeclass instances, like -- Category, Applicative, Functor, Arrow, -- etc., and also with the combinators in this library. You can build -- complex programs as a complex Auto by building up smaller and -- smaller components. See the tutorial for more information on this. -- -- This type also contains information on its own serialization, so you -- can serialize and re-load the internal state to binary or disk. See -- the "serialization" section in the documentation for -- Control.Auto.Core, or the documentation for mkAutoM for -- more details. data Auto m a b -- | Special case of Auto where the underlying Monad is -- Identity. -- -- Instead of "wrapping" an [a] -> m [b], it "wraps" an -- [a] -> [b]. type Auto' = Auto Identity -- | Returns a string representation of the internal constructor of the -- Auto. Useful for debugging the result of compositions and -- functions and seeing how they affect the internal structure of the -- Auto. -- -- In the order of efficiency, AutoFuncs tend to be faster than -- AutoStates tend to be faster than AutoArbs. However, -- when composing one with the other (using Category or -- Applicative), the two have to be "reduced" to the greatest -- common denominator; composing an AutoFunc with an -- AutoArb produces an AutoArb. -- -- More benchmarking is to be done to be able to rigorously say what -- these really mean, performance wise. autoConstr :: Auto m a b -> String -- | Re-structure Auto internals to use the Arb -- ("arbitrary") constructors, as recursion-based mealy machines. -- -- Mostly here for performance comparisons and benchmarking purposes. toArb :: Monad m => Auto m a b -> Auto m a b -- | In theory, "purifying" an Auto'" should prep it for faster -- evaluation when used with stepAuto' or streamAuto'. -- But the benchmarks have not been run yet, so stay tuned! -- -- TODO: Benchmark purifyAuto :: Auto' a b -> Auto' a b -- | Runs the Auto through one step. -- -- That is, given an Auto m a b, returns a function that -- takes an a and returns a b and an "updated"/"next" -- Auto; an a -> m (b, Auto m a b). -- -- This is the main way of running an Auto "step by step", so if -- you have some sort of game loop that updates everything every "tick", -- this is what you're looking for. At every loop, gather input -- a, feed it into the Auto, "render" the result -- b, and get your new Auto to run the next time. -- -- Here is an example with sumFrom 0, the Auto -- whose output is the cumulative sum of the inputs, and an underying -- monad of Identity. Here, -- --
-- stepAuto :: Auto Identity Int Int -- -> (Int -> Identity (Int, Auto Identity Int Int)) ---- -- Every time you "step", you give it an Int and get a resulting -- Int (the cumulative sum) and the "updated Auto", with -- the updated accumulator. -- --
-- >>> let a0 :: Auto Identity Int Int -- a0 = sumFrom 0 -- -- >>> let Identity (res1, a1) = stepAuto a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let Identity (res2, a2) = stepAuto a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let Identity (res3, _ ) = stepAuto a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- By the way, for the case where your Auto is under -- Identity, we have a type synomym Auto'...and a -- convenience function to make "running" it more streamlined: -- --
-- >>> let a0 :: Auto' Int Int -- a0 = sumFrom 0 -- -- >>> let (res1, a1) = stepAuto' a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let (res2, a2) = stepAuto' a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let (res3, _ ) = stepAuto' a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- But, if your Auto actaully has effects when being stepped, -- stepAuto will execute them: -- --
-- >>> let a0 :: Auto IO Int Int -- a0 = effect (putStrLn "hey!") *> sumFrom 0 -- -- >>> (res1, a1) <- stepAuto a0 4 -- run with 4 -- hey! -- IO effect -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> (res2, a2) <- stepAuto a1 5 -- run with 5 -- hey! -- IO effect -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> (res3, _ ) <- stepAuto a2 3 -- run with 3 -- hey! -- IO effect -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- (Here, effect (putStrLn "hey") is an -- Auto IO Int (), which ignores its input and just -- executes putStrLn "hey" every time it is run. When we -- use *> from Control.Applicative, we "combine" the two -- Autos together and run them both on each input (4, 5, -- 3...)...but for the "final" output at the end, we only return the -- output of the second one, sumFrom 0 (5, 9, 12...)) -- -- If you think of an Auto m a b as a "stateful function" -- a -> m b, then stepAuto lets you "run" it. -- -- In order to directly run an Auto on a stream, an [a], -- use streamAuto. That gives you an [a] -> m [b]. stepAuto :: Monad m => Auto m a b -> a -> m (b, Auto m a b) -- | Runs an Auto' through one step. -- -- That is, given an Auto' a b, returns a function that -- takes an a and returns a b and an "updated"/"next" -- Auto'; an a -> (b, Auto' a b). -- -- See stepAuto documentation for motivations, use cases, and more -- details. You can use this instead of stepAuto when your -- underyling monad is Identity, and your Auto doesn't -- produce any effects. -- -- Here is an example with sumFrom 0, the Auto' -- whose output is the cumulative sum of the inputs -- --
-- stepAuto' :: Auto' Int Int -- -> (Int -> (Int, Auto' Int Int)) ---- -- Every time you "step", you give it an Int and get a resulting -- Int (the cumulative sum) and the "updated Auto'", with -- the updated accumulator. -- --
-- >>> let a0 :: Auto' Int Int -- a0 = sumFrom 0 -- -- >>> let (res1, a1) = stepAuto' a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let (res2, a2) = stepAuto' a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let (res3, _ ) = stepAuto' a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- If you think of an Auto' a b as a "stateful function" -- a -> b, then stepAuto' lets you "run" it. -- -- In order to directly run an Auto' on a stream, an [a], -- use streamAuto'. That gives you an [a] -> [b]. stepAuto' :: Auto' a b -> a -> (b, Auto' a b) -- | Like stepAuto, but drops the "next Auto" and just gives -- the result. evalAuto :: Monad m => Auto m a b -> a -> m b -- | Like stepAuto', but drops the "next Auto'" and just -- gives the result. evalAuto for Auto'. evalAuto' :: Auto' a b -> a -> b -- | Like stepAuto, but drops the result and just gives the "updated -- Auto". execAuto :: Monad m => Auto m a b -> a -> m (Auto m a b) -- | Like stepAuto', but drops the result and just gives the -- "updated Auto'". execAuto for Auto'. execAuto' :: Auto' a b -> a -> Auto' a b -- | Encode an Auto and its internal state into a ByteString. encodeAuto :: Auto m a b -> ByteString -- | Resume an Auto from its ByteString serialization, -- giving a Left if the deserialization is not possible. decodeAuto :: Auto m a b -> ByteString -> Either String (Auto m a b) -- | Returns a Put --- instructions (from Data.Serialize) on -- how to "freeze" the Auto, with its internal state, and save it -- to a binary encoding. It can later be reloaded and "resumed" by -- 'resumeAuto'/'decodeAuto'. saveAuto :: Auto m a b -> Put -- | Returns a Get from an Auto --- instructions (from -- Data.Serialize) on taking a ByteString and "restoring" the -- originally saved Auto, in the originally saved state. resumeAuto :: Auto m a b -> Get (Auto m a b) -- | Takes an Auto that is serializable/resumable and returns an -- Auto that is not. That is, when it is "saved", saves no data, -- and when it is "resumed", resets itself back to the initial -- configuration every time; in other words, decodeAuto -- (unserialize a) bs = Right (unserialize a). Trying to "resume" it -- will just always give itself, unchanged. unserialize :: Monad m => Auto m a b -> Auto m a b -- | Swaps out the underlying Monad of an Auto using the -- given monad morphism "transforming function", a natural -- transformation. -- -- Basically, given a function to "swap out" any m a with an -- m' a, it swaps out the underlying monad of the Auto. -- -- This forms a functor, so you rest assured in things like this: -- --
-- hoistA id == id -- hoistA f a1 . hoistA f a2 == hoistA f (a1 . a2) --hoistA :: (Monad m, Monad m') => (forall c. m c -> m' c) -> Auto m a b -> Auto m' a b -- | Generalizes an Auto' a b to an Auto m a -- b' for any Monad m, using hoist. -- -- You generally should be able to avoid using this if you never directly -- write any Auto's and always write 'Auto m' parameterized over -- all Monads, but...in case you import one from a library or -- something, you can use this. generalizeA :: Monad m => Auto' a b -> Auto m a b -- | Abstraction over lower-level funging with serialization; lets you -- modify the result of an Auto by being able to intercept the -- (b, Auto m a b) output and return a new output value -- m c. -- -- Note that this is a lot like fmap: -- --
-- fmap :: (b -> c) -> Auto m a b -> Auto m a c ---- -- Except gives you access to both the b and the "updated -- Auto"; instead of an b -> c, you get to pass a -- (b, Auto m a b) -> m c. -- -- Basically experimenting with a bunch of abstractions over different -- lower-level modification of Autos, because making sure the -- serialization works as planned can be a bit difficult. interceptO :: Monad m => ((b, Auto m a b) -> m c) -> Auto m a b -> Auto m a c -- | Construct the Auto whose output is always the given value, -- ignoring its input. -- -- Provided for API constency, but you should really be using pure -- from the Applicative instance, from Control.Applicative, -- which does the same thing. mkConst :: b -> Auto m a b -- | Construct the Auto that always "executes" the given monadic -- value at every step, yielding the result as its output and ignoring -- its input. -- -- Provided for API consistency, but you shold really be using -- effect from Control.Auto.Effects, which does the same -- thing. mkConstM :: m b -> Auto m a b -- | Construct a stateless Auto that simply applies the given (pure) -- function to every input, yielding the output. The output stream is -- just the result of applying the function to every input. -- --
-- streamAuto' (mkFunc f) = map f ---- -- This is rarely needed; you should be using arr from the -- Arrow instance, from Control.Arrow. mkFunc :: (a -> b) -> Auto m a b -- | Construct a stateless Auto that simply applies and executes the -- givne (monadic) function to every input, yielding the output. The -- output stream is the result of applying the function to every input, -- executing/sequencing the action, and returning the returned value. -- --
-- streamAuto (mkFuncM f) = mapM f ---- -- It's recommended that you use arrM from -- Control.Auto.Effects. This is only really provided for -- consistency. mkFuncM :: (a -> m b) -> Auto m a b -- | Construct an Auto from a state transformer: an a -> s -- -> (b, s) gives you an Auto m a b, for any -- Monad m. At every step, it takes in the a -- input, runs the function with the stored internal state, returns the -- b result, and now contains the new resulting state. You have -- to intialize it with an initial state, of course. -- -- From the "stream transformer" point of view, this is rougly equivalent -- to mapAccumL from Data.List, with the function's -- arguments and results in the backwards order. -- --
-- streamAuto' (mkState f s0) = snd . mapAccumL (\s x -> swap (f x s)) ---- -- Try not to use this if it's ever avoidable, unless you're a framework -- developer or something. Try make something by combining/composing the -- various Auto combinators. -- -- If your state s does not have a Serialize instance, -- then you should either write a meaningful one, provide the -- serialization methods manually with mkState', or throw away -- serializability and use mkState_. mkState :: Serialize s => (a -> s -> (b, s)) -> s -> Auto m a b -- | A version of mkState, where the internal state isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See mkState for more details. -- -- Useful if your state s cannot have a meaningful -- Serialize instance. mkState_ :: (a -> s -> (b, s)) -> s -> Auto m a b -- | Construct an Auto from a "monadic" state transformer: a -- -> s -> m (b, s) gives you an Auto m a b. -- At every step, it takes in the a input, runs the function -- with the stored internal state and "executes" the m (b, s) to -- get the b output, and stores the s as the new, -- updated state. Must be initialized with an initial state. -- -- Try not to use this if it's ever avoidable, unless you're a framework -- developer or something. Try make something by combining/composing the -- various Auto combinators. -- -- This version is a wrapper around mkAuto, that keeps track of -- the serialization and re-loading of the internal state for you, so you -- don't have to deal with it explicitly. -- -- If your state s does not have a Serialize instance, -- then you should either write a meaningful one, provide the -- serialization methods manually with mkStateM', or throw away -- serializability and use mkStateM_. mkStateM :: Serialize s => (a -> s -> m (b, s)) -> s -> Auto m a b -- | A version of mkStateM, where the internal state isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See mkStateM for more details. -- -- Useful if your state s cannot have a meaningful -- Serialize instance. mkStateM_ :: (a -> s -> m (b, s)) -> s -> Auto m a b -- | A version of mkState, where the internal state doesn't have a -- Serialize instance, so you provide your own instructions for -- getting and putting the state. -- -- See mkState for more details. mkState' :: Get s -> (s -> Put) -> (a -> s -> (b, s)) -> s -> Auto m a b -- | A version of mkStateM, where the internal state doesn't have a -- Serialize instance, so you provide your own instructions for -- getting and putting the state. -- -- See mkStateM for more details. mkStateM' :: Get s -> (s -> Put) -> (a -> s -> m (b, s)) -> s -> Auto m a b -- | Construct an Auto from a "folding" function: b -> a -- -> b yields an Auto m a b. Basically acts like -- a foldl or a scanl. There is an internal accumulator -- that is "updated" with an a at every step. Must be given an -- initial accumulator. -- -- Example: an Auto that sums up all of its input. -- --
-- >>> let summer = accum (+) 0 -- -- >>> let (sum1, summer') = stepAuto' summer 3 -- -- >>> sum1 -- 3 -- -- >>> let (sum2, summer'') = stepAuto' summer' 10 -- -- >>> sum2 -- 13 -- -- >>> streamAuto' summer'' [1..10] -- [14,16,19,23,28,34,41,49,58,68] ---- -- If your accumulator b does not have a Serialize -- instance, then you should either write a meaningful one, or throw away -- serializability and use accum_. accum :: Serialize b => (b -> a -> b) -> b -> Auto m a b -- | A version of accum, where the internal accumulator isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See accum for more details. -- -- Useful if your accumulator b cannot have a meaningful -- Serialize instance. accum_ :: (b -> a -> b) -> b -> Auto m a b -- | Construct an Auto from a "monadic" "folding" function: b -- -> a -> m b yields an Auto m a b. Basically -- acts like a foldM or scanM (if it existed). here is an -- internal accumulator that is "updated" with an input a with -- the result of the executed m b at every step. Must be given -- an initial accumulator. -- -- See accum for more details. -- -- If your accumulator b does not have a Serialize -- instance, then you should either write a meaningful one, or throw away -- serializability and use accumM_. accumM :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b -- | A version of 'accumM_, where the internal accumulator isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See accumM for more details. -- -- Useful if your accumulator b cannot have a meaningful -- Serialize instance. accumM_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b -- | A "delayed" version of accum, where the first output is the -- initial state of the accumulator, before applying the folding -- function. Useful in recursive bindings. -- --
-- >>> let summerD = accumD (+) 0 -- -- >>> let (sum1, summerD') = stepAuto' summerD 3 -- -- >>> sum1 -- 0 -- -- >>> let (sum2, summerD'') = stepAuto' summerD' 10 -- -- >>> sum2 -- 3 -- -- >>> streamAuto' summerD'' [1..10] -- [13,14,16,19,23,28,34,41,49,58] ---- -- (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) -> b -> Auto m a b -- | The non-resuming/non-serializing version of accumD. accumD_ :: (b -> a -> b) -> b -> Auto m a b -- | A "delayed" version of accumM, where the first output is the -- initial state of the accumulator, before applying the folding -- function. Useful in recursive bindings. accumMD :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of accumMD. accumMD_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b -- | Construct an Auto by explicity giving its serialization, -- deserialization, and the function from a to a b and -- "updated Auto". -- -- Ideally, you wouldn't have to use this unless you are making your own -- framework. Try your best to make what you want by assembling primtives -- together. Working with serilization directly is hard. -- -- See mkAutoM for more detailed instructions on doing this right. mkAuto :: Get (Auto m a b) -> Put -> (a -> (b, Auto m a b)) -> Auto m a b -- | Like mkAuto, but without any way of meaningful serializing or -- deserializing. -- -- Be careful! This Auto can still carry arbitrary internal state, -- but it cannot be meaningfully serialized or re-loaded/resumed. You can -- still pretend to do so using -- 'resumeAuto'/'saveAuto'/'encodeAuto'/'decodeAuto' (and the type system -- won't stop you), but when you try to "resume"/decode it, its state -- will be lost. mkAuto_ :: (a -> (b, Auto m a b)) -> Auto m a b -- | Construct an Auto by explicitly giving its serializiation, -- deserialization, and the (monadic) function from a to a -- b and the "updated Auto". -- -- See the "serialization" section in the Control.Auto.Core module -- for more information. -- -- Ideally, you wouldn't have to use this unless you are making your own -- framework. Try your best to make what you want by assembling primtives -- together. -- -- But sometimes you have to write your own combinators, and you're going -- to have to use mkAutoM to make it work. -- -- Sometimes, it's simple: -- --
-- fmap :: (a -> b) -> Auto r a -> Auto r b -- fmap f a0 = mkAutoM (do aResumed <- resumeAuto a0 -- return (fmap f aResumed) ) -- (saveAuto a0) -- $ x -> do -- (y, a1) <- stepAuto a0 x -- return (f y, fmap f a1) ---- -- Serializing fmap f a0 is just the same as serializing -- a0. And to resume it, we resume a0 to get a resumed -- version of a0, and then we apply fmap f to -- the Auto that we resumed. -- -- Also another nice "simple" example is: -- --
-- catchA :: Exception e -- => Auto IO a b -- -> Auto IO a (Either e b) -- catchA a = mkAutoM (do aResumed <- resumeAuto a -- return (catchA aResumed) ) -- (saveAuto a) -- $ x -> do -- eya' <- try $ stepAuto a x -- case eya' of -- Right (y, a') -> return (Right y, catchA a') -- Left e -> return (Left e , catchA a ) ---- -- Which is basically the same principle, in terms of serializing and -- resuming strategies. -- -- When you have "switching" --- things that behave like different -- Autos at different points in time --- then things get a little -- complicated, because you have to figure out which Auto to -- resume. -- -- For example, let's look at the source of -?>: -- --
-- (-?>) :: Monad m -- => Interval m a b -- ^ initial behavior -- -> Interval m a b -- ^ final behavior, when the initial -- -- behavior turns off. -- -> Interval m a b -- a1 -?> a2 = mkAutoM l s t -- where -- l = do -- flag <- get -- if flag -- then resumeAuto (switched a2) -- else (-?> a2) $ resumeAuto a1 -- s = put False *> saveAuto a1 -- t x = do -- (y1, a1') <- stepAuto a1 x -- case y1 of -- Just _ -> -- return (y1, a1' -?> a2) -- Nothing -> do -- (y, a2') <- stepAuto a2 x -- return (y, switched a2') -- switched a = mkAutoM l -- (put True *> saveAuto a) -- $ x -> do -- (y, a') <- stepAuto a x -- return (y, switched a') ---- -- We have to invent a serialization and reloading scheme, taking into -- account the two states that the resulting Auto can be in: -- --
-- zipAuto :: Monad m -- => a -- ^ default input value -- -> [Auto m a b] -- ^ Autos to zip up -- -> Auto m [a] [b] -- zipAuto x0 as = mkAutoM (zipAuto x0 $ mapM resumeAuto as) -- (mapM_ saveAuto as) -- $ xs -> do -- res <- zipWithM stepAuto as (xs ++ repeat x0) -- let (ys, as') = unzip res -- return (ys, zipAuto x0 as') ---- -- To serialize, we basically sequence saveAuto over all of the -- internal Autos --- serialize each of their serialization data -- one-by-one one after the other in our binary. -- -- To load, we do the same thing; we go over every Auto in -- as and resumeAuto it, and then collect the results in -- a list --- a list of resumed Autos. And then we apply -- zipAuto x0 to that list of Autos, to get our -- resumed zipAuto x0 as. -- -- So, it might be complicated. In the end, it might be all worth it, -- too, to have implicit serialization compose like this. Think about -- your serialization strategy first. Step back and think about what you -- need to serialize at every step, and remember that it's _the initial_ -- "resuming" function that has to "resume everything"...it's not the -- resuming function that exists when you finally save your Auto, -- it's the resuming Get that was there at the beginning. -- For -?>, the intial l had to know how to "skip -- ahead". -- -- And of course as always, test. -- -- If you need to make your own combinator or transformer but are having -- trouble with the serializtion, feel free to contact me at -- justin@jle.im, on freenode at #haskell or -- #haskell-auto, open a github issue, etc. Just contact me -- somehow, I'll be happy to help! mkAutoM :: Get (Auto m a b) -> Put -> (a -> m (b, Auto m a b)) -> Auto m a b -- | Like mkAutoM, but without any way of meaningful serializing or -- deserializing. -- -- Be careful! This Auto can still carry arbitrary internal state, -- but it cannot be meaningfully serialized or re-loaded/resumed. You can -- still pretend to do so using -- 'resumeAuto'/'saveAuto'/'encodeAuto'/'decodeAuto' (and the type system -- won't stop you), but when you try to "resume"/decode it, its state -- will be reset. mkAutoM_ :: (a -> m (b, Auto m a b)) -> Auto m a b -- | Force the serializing components of an Auto. -- -- TODO: Test if this really works forceSerial :: Auto m a b -> Auto m a b -- | A special Auto that acts like the id Auto, but -- forces results as they come through to be fully evaluated, when -- composed with other Autos. -- -- TODO: Test if this really works forcer :: NFData a => Auto m a a -- | A special Auto that acts like the id Auto, but -- forces results as they come through to be evaluated to Weak Head -- Normal Form, with seq, when composed with other Autos. -- -- TODO: Test if this really works seqer :: Auto m a a instance Typeable Auto instance (Monad m, Floating b) => Floating (Auto m a b) instance (Monad m, Fractional b) => Fractional (Auto m a b) instance (Monad m, Num b) => Num (Auto m a b) instance (Monad m, IsString b) => IsString (Auto m a b) instance (Monad m, Monoid b) => Monoid (Auto m a b) instance (Monad m, Semigroup b) => Semigroup (Auto m a b) instance MonadFix m => ArrowLoop (Auto m) instance Monad m => ArrowChoice (Auto m) instance Monad m => Arrow (Auto m) instance MonadFix m => Costrong (Auto m) instance Monad m => Choice (Auto m) instance Monad m => Strong (Auto m) instance Monad m => Profunctor (Auto m) instance Monad m => Category (Auto m) instance (Monad m, Alternative m) => Alternative (Auto m a) instance Monad m => Applicative (Auto m a) instance Monad m => Functor (Auto m a) -- | This module provides combinators and utilities for working with the -- semantic concept of "intervals": an Auto whose output stream is -- "on" or "off" for (conceputally) contiguous chunks of time. module Control.Auto.Interval -- | Represents a relationship between an input and an output, where the -- output can be "on" or "off" (using Just and Nothing) for -- contiguous chunks of time. -- -- Just a type alias for Auto m a (Maybe -- b). If you ended up here with a link...no worries! If you see -- Interval m a b, just think Auto m a -- (Maybe b) for type inference/type checking purposes. -- -- If you see something of type Interval, you can rest assured -- that it has "interval semantics" --- it is on and off for meaningfully -- contiguous chunks of time, instead of just on and off willy nilly. If -- you have a function that expects an Interval, then the function -- expects its argument to behave in this way. type Interval m a b = Auto m a (Maybe b) -- | Interval, specialized with Identity as its underlying -- Monad. Analogous to Auto' for Auto. type Interval' a b = Auto' a (Maybe b) -- | The output stream is alwayas off, regardless of the input. -- -- Note that any monadic effects of the input Auto when composed -- with off are still executed, even though their result value is -- suppressed. -- --
-- off == pure Nothing --off :: Interval m a b -- | The output stream is always on, with exactly the value of the -- corresponding input. -- --
-- toOn == arr Just --toOn :: Interval m a a -- | An "interval collapsing" Auto. A stream of on/off values comes -- in; the output is the value of the input when the input is on, and the -- "default value" when the input is off. -- -- Much like fromMaybe from Data.Maybe. -- --
-- fromInterval d = arr (fromMaybe d) --fromInterval :: a -> Auto m (Maybe a) a -- | An "interval collapsing" Auto. A stream of on/off values comes -- in; when the input is off, the output is the "default value". When the -- input is off, the output is the given function applied to the "on" -- value. -- -- Much like maybe from Data.Maybe. -- --
-- fromIntervalWith d f = arr (maybe d f) --fromIntervalWith :: b -> (a -> b) -> Auto m (Maybe a) b -- | For onFor n, the first n items in the output -- stream are always "on" (passing through with exactly the value of the -- corresponding input); for the rest, the output stream is always "off", -- suppressing all input values forevermore. -- -- If a number less than 0 is passed, 0 is used. onFor :: Int -> Interval m a a -- | For offFor n, the first n items in the output -- stream are always "off", suppressing all input; for the rest, the -- output stream is always "on", outputting exactly the value of the -- corresponding input. offFor :: Int -> Interval m a a -- | A combination of onFor and offFor; for window -- b e, the output stream will be "on" from item b to item -- e inclusive with the value of the corresponding input; for -- all other times, the output stream is always off, suppressing any -- input. window :: Int -> Int -> Interval m a a -- | The output is "on" with exactly the value of he corresponding input -- when the input passes the predicate, and is "off" otherwise. -- --
-- >>> let a = whenI (\x -> x >= 2 && x <= 4) -- -- >>> streamAuto' a [1..6] -- [Nothing, Just 2, Just 3, Just 4, Nothing, Nothing] ---- -- Careful when using this; you could exactly create an Interval -- that "breaks" "interval semantics"; for example, 'whenI even', when -- you know your input stream does not consist of chunks of even numbers -- and odd numbers at a time. whenI :: (a -> Bool) -> Interval m a a -- | Like whenI, but only allows values to pass whenever the input -- does not satisfy the predicate. Blocks whenever the predicate is true. -- --
-- >>> let a = unlessI (\x -> x < 2 &&& x > 4) -- -- >>> steamAuto' a [1..6] -- -- >>> res -- [Nothing, Just 2, Just 3, Just 4, Nothing, Nothing] --unlessI :: (a -> Bool) -> Interval m a a -- | Forks a common input stream between an Interval and an -- Auto, and returns, itself, a normal non-interval Auto.. -- If the output of the first one is "on", the output of the whole thing -- is that "on" value. Otherwise, the output is exactly that of the -- second one. -- --
-- >>> let a1 = (onFor 2 . pure "hello") <|!> pure "world" -- -- >>> take 5 . streamAuto' a1 $ repeat () -- ["hello", "hello", "world", "world", "world"] ---- -- This one is neat because it associates from the right, so it can be -- "chained": -- --
-- >>> let a2 = onFor 2 . pure "hello" -- <|!> onFor 4 . pure "world" -- <|!> pure "goodbye!" -- -- >>> take 6 . streamAuto' a2 $ repeat () -- ["hello", "hello", "world", "world", "goodbye!", "goodbye!"] ---- --
-- a <|!> b <|!> c ---- -- associates as -- --
-- a <|!> (b <|!> c) ---- -- So using this, you can "chain" a bunch of choices between intervals, -- and then at the right-most, "final" one, provide the default behavior. -- -- Warning: If your underlying monad produces effects, remember that -- both Autos are run at every step, along with any monadic -- effects, regardless of whether they are "on" or "off". (<|!>) :: Monad m => Interval m a b -> Auto m a b -> Auto m a b -- | Forks a common input stream between the two Intervals and -- returns, itself, an Interval. If the output of the first one is -- "on", the whole thing is on with that output. Otherwise, the output is -- exactly that of the second one. -- --
-- >>> let a = (onFor 2 . pure "hello") <|?> (onFor 4 . pure "world") -- -- >>> take 5 . streamAuto' a $ repeat () -- -- >>> res -- [Just "hello", Just "hello", Just "world", Just "world", Nothing] ---- -- You can drop the parentheses, because of precedence; the above could -- have been written as: -- --
-- >>> let a' = onFor 2 . pure "hello" <|?> onFor 4 . pure "world" ---- -- Warning: If your underlying monad produces effects, remember that -- both Autos are run at every step, along with any monadic -- effects, regardless of whether they are "on" or "off". -- -- Note that more often than not, <|!> is probably more -- useful. This is useful only in the case that you really, really want -- an interval at the end of it all. (<|?>) :: Monad m => Interval m a b -> Interval m a b -> Interval m a b -- | Forks an input stream between all Intervals in the list. The -- result is an Interval whose output is "on" when any of the -- original Intervals is on, with the value of the first -- "on" one. -- --
-- chooseInterval == foldr (<|?>) off --chooseInterval :: Monad m => [Interval m a b] -> Interval m a b -- | Forks an input stream between all Intervals in the list, plus a -- "default Auto. The output is the value of the first "on" -- Interval; if there isn't any, the output from the "default -- Auto" is used. -- --
-- choose == foldr (<|!>) --choose :: Monad m => Auto m a b -> [Interval m a b] -> Auto m a b -- | Takes two input streams --- a stream of normal values, and a blip -- stream. Before the first emitted value of the input blip stream, the -- output is always "off", suppressing all inputs. After the first -- emitted value of the input blip stream, the output is always "on" with -- the corresponding value of the first input stream. -- --
-- >>> let a = after . (count &&& inB 3) -- -- >>> take 6 . streamAuto' a $ repeat () -- -- >>> res -- [Nothing, Nothing, Just 3, Just 4, Just 5, Just 6] ---- -- (count is the Auto that ignores its input and outputs -- the current step count at every step, and inB 3 is -- the Auto generating a blip stream that emits at the third -- step.) -- -- Be careful to remember that in the above example, count is -- still "run" at every step, and is progressed (and if it were an -- Auto with monadic effects, they would still be executed). It -- just isn't allowed to pass its output values through after -- until the blip stream emits. after :: Interval m (a, Blip b) a -- | Takes two input streams --- a stream of normal values, and a blip -- stream. Before the first emitted value of the input blip stream, the -- output is always "on" with the corresponding value of the first input -- stream. After the first emitted value of the input blip stream, -- the output will be "off" forever, suppressing all input. -- --
-- >>> let a = before . (count &&& inB 3) -- -- >>> take 5 . streamAuto' a $ repeat () -- -- >>> res -- [Just 1, Just 2, Nothing, Nothing, Nothing] ---- -- (count is the Auto that ignores its input and outputs -- the current step count at every step, and inB 3 is -- the Auto generating a blip stream that emits at the third -- step.) -- -- Be careful to remember that in the above example, count is -- still "run" at every step, and is progressed (and if it were an -- Auto with monadic effects, they would still be executed). It -- just isn't allowed to pass its output values through before -- after the blip stream emits. before :: Interval m (a, Blip b) a -- | Takes three input streams: a stream of normal values, a blip stream of -- "turning-on" blips, and a blip stream of "turning-off" blips. After -- the first blip stream emits, the output will switch to "on" with the -- value of the first input stream. After the second blip stream emits, -- the output will switch to "off", supressing all inputs. An emission -- from the first stream toggles this "on"; an emission from the second -- stream toggles this "off". -- --
-- >>> let a = between . (count &&& (inB 3 &&& inB 5)) -- -- >>> take 7 . streamAuto' a $ repeat () -- [Nothing, Nothing, Just 3, Just 4, Nothing, Nothing, Nothing] --between :: Interval m (a, (Blip b, Blip c)) a -- | The output is constantly "on" with the last emitted value of the input -- blip stream. However, before the first emitted value, it is "off". -- value of the input blip stream. From then on, the output is always the -- last emitted value -- --
-- >>> let a = hold . inB 3 -- -- >>> streamAuto' a [1..5] -- [Nothing, Nothing, Just 3, Just 3, Just 3] ---- -- If you want an Auto m (Blip a) a (no -- Nothing...just a "default value" before everything else), then -- you can use holdWith from Control.Auto.Blip...or also -- just hold with <|!> or fromInterval. hold :: Serialize a => Interval m (Blip a) a -- | The non-serializing/non-resuming version of hold. hold_ :: Interval m (Blip a) a -- | For holdFor n, The output is only "on" if there was an -- emitted value from the input blip stream in the last n steps. -- Otherwise, is off. -- -- Like hold, but it only "holds" the last emitted value for the -- given number of steps. -- --
-- >>> let a = holdFor 2 . inB 3 -- -- >>> streamAuto' 7 a [1..7] -- -- >>> res -- [Nothing, Nothing, Just 3, Just 3, Nothing, Nothing, Nothing] --holdFor :: Serialize a => Int -> Interval m (Blip a) a -- | The non-serializing/non-resuming version of holdFor. holdFor_ :: Int -> Interval m (Blip a) a -- | Stretches the last "on"/Just input over the entire range -- of following "off"/Nothing inputs. Holds on to the last -- Just until another one comes along. -- --
-- >>> streamAuto' holdJusts [Nothing, Just 1, Just 3, Nothing, Nothing, Just 5] -- [Nothing, Just 1, Just 3, Just 3, Just 3, Just 5] --holdJusts :: Serialize a => Interval m (Maybe a) a -- | The non-resuming/non-serializing version of holdJusts. holdJusts_ :: Interval m (Maybe a) a -- | Lifts an Auto m a b (transforming as -- into bs) into an Auto m (Maybe a) -- (Maybe b) (or, Interval m (Maybe a) -- b, transforming intervals of as into -- intervals of b. -- -- It does this by running the Auuto as normal when the input is -- "on", and freezing itbeing "off" when the input is off/. -- --
-- >>> let a1 = during (sumFrom 0) . onFor 2 . pure 1 -- -- >>> take 5 . streamAuto' a1 $ repeat () -- [Just 1, Just 2, Nothing, Nothing, Nothing] ---- --
-- >>> let a2 = during (sumFrom 0) . offFor 2 . pure 1 -- -- >>> take 5 . streamAuto' a2 $ repeat () -- [Nothing, Nothing, Just 1, Just 2, Just 3] ---- -- (Remember that pure x is the Auto that ignores -- its input and constantly just pumps out x at every step) -- -- Note the difference between putting the sumFrom "after" the -- offFor in the chain with during (like the previous -- example) and putting the sumFrom "before": -- --
-- >>> let a3 = offFor 2 . sumFrom 0 . pure 1 -- -- >>> take 5 . streamAuto' a3 $ repeat () -- [Nothing, Nothing, Just 3, Just 4, Just 5] ---- -- In the first case (with a2), the output of pure -- 1 was suppressed by offFor, and during -- (sumFrom 0) was only summing on the times that the 1's -- were "allowed through"...so it only "starts counting" on the third -- step. -- -- In the second case (with a3), the output of the -- pure 1 is never suppressed, and went straight into the -- sumFrom 0. sumFrom is always summing, the -- entire time. The final output of that sumFrom 0 is -- suppressed at the end with offFor 2. during :: Monad m => Auto m a b -> Interval m (Maybe a) b -- | Composes two Intervals, the same way that . composes two -- Autos: -- --
-- (.) :: Auto m b c -> Auto m a b -> Auto m a c -- compI :: Interval m b c -> Interval m a b -> Interval m a c ---- -- Basically, if any Interval in the chain is "off", then the -- entire rest of the chain is "skipped", short-circuiting a la -- Maybe. -- -- (Users of libraries with built-in inhibition semantics like Yampa and -- netwire might recognize this as the "default" composition in those -- other libraries) -- -- As a contrived example, how about an Auto that only allows -- values through during a window...between, say, the second and fourth -- steps: -- --
-- >>> let window' start dur = onFor dur `compI` offFor (start - 1) -- -- >>> streamAuto' (window' 2 3) -- [Nothing, Just 2, Just 3, Just 4, Nothing, Nothing] --compI :: Monad m => Interval m b c -> Interval m a b -> Interval m a c -- | Lifts (more technically, "binds") an Interval m a -- b into an Interval m (Maybe a) b. -- -- Does this by running the Auto as normal when the input is "on", -- and freezing itbeing "off" when the input is off/. -- -- It's kind of like during, but the resulting Maybe -- (Maybe b)) is "joined" back into a Maybe -- b. -- --
-- bindI a == fmap join (during a) ---- -- This is really an alternative formulation of compI; typically, -- you will be using compI more often, but this form can also be -- useful (and slightly more general). Note that: -- --
-- bindI f == compI f id ---- -- This combinator allows you to properly "chain" ("bind") together -- series of inhibiting Autos. If you have an Interval -- m a b and an Interval m b c, you can chain them -- into an Interval m a c. -- --
-- f :: Interval m a b -- g :: Interval m b c -- bindI g . f :: Interval m a c ---- -- (Users of libraries with built-in inhibition semantics like Yampa and -- netwire might recognize this as the "default" composition in those -- other libraries) -- -- See compI for examples of this use case. bindI :: Monad m => Interval m a b -> Interval m (Maybe a) b -- | This module contains various Autos that act as "producing" -- streams; they all ignore their input streams and produce output -- streams through a pure or monadic process. module Control.Auto.Generate -- | An Interval that ignores the input stream and just outputs -- items from the given list. Is "on" as long as there are still items in -- the list left, and "off" after there is nothing left in the list to -- output. -- -- Serializes itself by storing the entire rest of the list in binary, so -- if your list is long, it might take up a lot of space upon storage. If -- your list is infinite, it makes an infinite binary, so be careful! -- -- fromLongList can be used for longer lists or infinite lists; -- or, if your list can be boild down to an unfoldr, you can use -- unfold. -- --
-- pure :: b -> Auto m a b -- effect :: m b -> Auto m a b ---- -- The output of pure is always the same, and the output of -- effect is always the result of the same monadic action. Both -- ignore their inputs. -- -- Fun times when the underling Monad is, for instance, -- Reader. -- --
-- >>> let a = effect ask :: Auto (Reader b) a b -- -- >>> let r = evalAuto a () :: Reader b b -- -- >>> runReader r "hello" -- "hello" -- -- >>> runReader r 100 -- 100 ---- -- If your underling monad has effects (IO, State, -- Maybe, Writer, etc.), then it might be fun to take -- advantage of *> from Control.Applicative to "tack on" -- an effect to a normal Auto: -- --
-- >>> let a = effect (modify (+1)) *> sumFrom 0 :: Auto (State Int) Int Int -- -- >>> let st = streamAuto a [1..10] -- -- >>> let (ys, s') = runState st 0 -- -- >>> ys -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> s' -- 10 ---- -- Out Auto a behaves exactly like sumFrom -- 0, except at each step, it also increments the underlying/global -- state by one. It is sumFrom 0 with an "attached -- effect". effect :: m b -> Auto m a b -- | Analogous to iterate from Prelude. Keeps accumulator -- value and continually applies the function to the accumulator at every -- step, outputting the result. -- -- The first result is the initial accumulator value. -- --
-- >>> take 10 . streamAuto' (iterator (*2) 1) $ repeat () -- [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] --iterator :: Serialize b => (b -> b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of iterator. iterator_ :: (b -> b) -> b -> Auto m a b -- | Like iterator, but with a monadic function. iteratorM :: (Serialize b, Monad m) => (b -> m b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of iteratorM. iteratorM_ :: Monad m => (b -> m b) -> b -> Auto m a b -- | Given a function from discrete enumerable inputs, iterates through all -- of the results of that function. -- --
-- >>> take 10 . streamAuto' (discreteF (^2) 0) $ repeat () -- [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] --discreteF :: (Enum c, Serialize c) => (c -> b) -> c -> Auto m a b -- | The non-resuming/non-serializing version of discreteF. discreteF_ :: Enum c => (c -> b) -> c -> Auto m a b -- | Analogous to unfoldr from Prelude. Creates an -- Interval (that ignores its input) by maintaining an internal -- accumulator of type c and, at every step, applying to the -- unfolding function to the accumulator. If the result is -- Nothing, then the Interval will turn "off" forever -- (output Nothing forever); if the result is Just (y, -- acc), then it will output y and store acc as -- the new accumulator. -- -- Given an initial accumulator. -- --
-- >>> let countFromTil n m = flip unfold n $ \i -> if i <= m -- then Just (i, i+1) -- else Nothing -- -- >>> take 8 . streamAuto' (countFromTil 5 10) $ repeat () -- [Just 5, Just 6, Just 7, Just 8, Just 9, Just 10, Nothing, Nothing] ---- -- unfold f c0 behaves like overList -- (unfoldr f c0). unfold :: Serialize c => (c -> Maybe (b, c)) -> c -> Interval m a b -- | The non-resuming & non-serializing version of unfold. unfold_ :: (c -> Maybe (b, c)) -> c -> Interval m a b -- | Like unfold, but the unfolding function is monadic. unfoldM :: (Serialize c, Monad m) => (c -> m (Maybe (b, c))) -> c -> Interval m a b -- | The non-resuming & non-serializing version of unfoldM. unfoldM_ :: Monad m => (c -> m (Maybe (b, c))) -> c -> Interval m a b -- | Continually enumerate from the starting value, using succ. enumFromA :: (Serialize b, Enum b) => b -> Auto m a b -- | The non-serializing/non-resuming version of enumFromA. enumFromA_ :: Enum b => b -> Auto m a b -- | Various Autos describing relationships following common -- processes, like sumFrom, whose output is the cumulative sum of -- the input. -- -- Also has some Auto constructors inspired from digital signal -- processing signal transformation systems and statistical models. -- -- Note that all of these can be turned into an equivalent version acting -- on blip streams, with perBlip: -- --
-- sumFrom n :: Num a => Auto m a a -- perBlip (sumFrom n) :: Num a => Auto m (Blip a) (Blip a) --module Control.Auto.Process -- | The stream of outputs is the cumulative/running sum of the inputs so -- far, starting with an initial count. -- -- The first output takes into account the first input. See -- sumFromD for a version where the first output is the initial -- count itself. -- --
-- sumFrom x0 = accum (+) x0 --sumFrom :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of sumFrom. sumFrom_ :: Num a => a -> Auto m a a -- | Like sumFrom, except the first output is the starting count. -- --
-- >>> let a = sumFromD 5 -- -- >>> let (y1, a') = stepAuto' a 10 -- -- >>> y1 -- 5 -- -- >>> let (y2, _ ) = stepAuto' a' 3 -- -- >>> y2 -- 10 ---- --
-- >>> streamAuto' (sumFrom 0) [1..10] -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> streamAuto' (sumFromD 0) [1..10] -- [0,1,3,6,10,15,21,28,36,45] ---- -- It's sumFrom, but "delayed". -- -- Useful for recursive bindings, where you need at least one value to be -- able to produce its "first output" without depending on anything else. -- --
-- sumFromD x0 = sumFrom x0 . delay 0 ---- --
-- sumFromD x0 = delay x0 . sumFrom x0 --sumFromD :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of sumFromD. sumFromD_ :: Num a => a -> Auto m a a -- | The output is the running/cumulative product of all of the inputs so -- far, starting from an initial product. -- --
-- productFrom x0 = accum (*) x0 --productFrom :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of productFrom. productFrom_ :: Num a => a -> Auto m a a -- | The output is the the difference between the input and the previously -- received input. -- -- First result is a Nothing, so you can use <|!> or -- fromInterval or fromMaybe to get a "default first -- value". -- --
-- >>> streamAuto' deltas [1,6,3,5,8] -- -- >>> [Nothing, Just 5, Just (-3), Just 2, Just 3] ---- -- Usage with <|!>: -- --
-- >>> let a = deltas <|!> pure 100 -- -- >>> streamAuto' (deltas <|!> pure 100) [1,6,3,5,8] -- [100, 5, -3, 2, 3] ---- -- Usage with fromMaybe: -- --
-- >>> streamAuto' (fromMaybe 100 <$> deltas) [1,6,3,5,8] -- [100, 5, -3, 2, 3] --deltas :: (Serialize a, Num a) => Interval m a a -- | The non-resuming/non-serializing version of deltas. deltas_ :: Num a => Interval m a a -- | The output is the sum of the past inputs, multiplied by a moving -- window of weights. -- -- For example, if the last received inputs are [1,2,3,4] (from -- most recent to oldest), and the window of weights is -- [2,0.5,4], then the output will be 1*2 + 0.5*2 + -- 4*3, or 15. (The weights are assumed to be zero past the -- end of the weight window) -- -- The immediately received input is counted as a part of the history. -- -- Mathematically, y_n = w_0 * x_(n-0) + w_1 + x_(n-1) + w_2 * -- x_(n-1) + ..., for all ws in the weight window, where -- the first item is w_0. y_n is the nth -- output, and x_n is the nth input. -- -- Note that this serializes the history of the input...or at least the -- history as far back as the entire window of weights. (A weight list of -- five items will serialize the past five received items) If your weight -- window is very long (or infinite), then serializing is a bad idea! -- -- The second parameter is a list of a "starting history", or initial -- conditions, to be used when the actual input history isn't long -- enough. If you want all your initial conditions/starting history to be -- 0, just pass in []. -- -- Minus serialization, you can implement sumFrom as: -- --
-- sumFrom n = movingAverage (repeat 1) [n] ---- -- And you can implement a version of deltas as: -- --
-- deltas = movingAverage [1,-1] [] ---- -- It behaves the same, except the first step outputs the initially -- received value. So it's realy a bit like -- --
-- (movingAverage [1,-1] []) == (deltas |! id) ---- -- Where for the first step, the actual input is used instead of the -- delta. -- -- Name comes from the statistical model. movingAverage :: (Num a, Serialize a) => [a] -> [a] -> Auto m a a -- | The non-serializing/non-resuming version of movingAverage. movingAverage_ :: Num a => [a] -> [a] -> Auto m a a -- | Any linear time independent stream transformation can be encoded by -- the response of the transformation when given [1,0,0,0...], -- or 1 : repeat 0. So, given an LTI Auto, -- if you feed it 1 : repeat 0, the output is what is -- called an "impulse response function". -- -- For any LTI Auto, we can reconstruct the behavior of the -- original Auto given its impulse response. Give -- impulseResponse an impulse response, and it will -- recreate/reconstruct the original Auto. -- --
-- >>> let getImpulseResponse a = streamAuto' a (1 : repeat 0) -- -- >>> let sumFromImpulseResponse = getImpulseResponse (sumFrom 0) -- -- >>> streamAuto' (sumFrom 0) [1..10] -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> streamAuto' (impulseResponse sumFromImpulseResponse) [1..10] -- [1,3,6,10,15,21,28,36,45,55] ---- -- Use this function to create an LTI system when you know its impulse -- response. -- --
-- >>> take 10 . streamAuto' (impulseResponse (map (2**) [0,-1..])) $ repeat 1 -- [1.0,1.5,1.75,1.875,1.9375,1.96875,1.984375,1.9921875,1.99609375,1.998046875] ---- -- All impulse response after the end of the given list is assumed to be -- zero. -- -- Mathematically, y_n = h_0 * x_(n-0) + h_1 + x_(n-1) + h_2 * -- x_(n-1) + ..., for all h_n in the input response, where -- the first item is h_0. -- -- Note that when this is serialized, it must serialize a number of input -- elements equal to the length of the impulse response list...so if you -- give an infinite impulse response, you might want to use -- impulseResponse_, or not serialize. -- -- By the way, impulseResponse ir == movingAverage ir -- []. impulseResponse :: (Num a, Serialize a) => [a] -> Auto m a a -- | The non-serializing/non-resuming version of impulseResponse. impulseResponse_ :: Num a => [a] -> Auto m a a -- | The output is the sum of the past outputs, multiplied by a moving -- window of weights. Ignores all input. -- -- For example, if the last outputs are [1,2,3,4] (from most -- recent to oldest), and the window of weights is [2,0.5,4], -- then the output will be 1*2 + 0.5*2 + 4*3, or 15. -- (The weights are assumed to be zero past the end of the weight window) -- -- Mathematically, y_n = w_1 * y_(n-1) + w_2 * y_(n-2) + ..., -- for all w in the weight window, where the first item is -- w_1. -- -- Note that this serializes the history of the outputs...or at least the -- history as far back as the entire window of weights. (A weight list of -- five items will serialize the past five outputted items) If your -- weight window is very long (or infinite), then serializing is a bad -- idea! -- -- The second parameter is a list of a "starting history", or initial -- conditions, to be used when the actual output history isn't long -- enough. If you want all your initial conditions/starting history to be -- 0, just pass in []. -- -- You can use this to implement any linear recurrence relationship, like -- he fibonacci sequence: -- --
-- >>> evalAutoN' 10 (autoRegression [1,1] [1,1]) () -- [2,3,5,8,13,21,34,55,89,144] -- -- >>> evalAutoN' 10 (fromList [1,1] --> autoRegression [1,1] [1,1]) () -- [1,1,2,3,5,8,13,21,34,55] ---- -- Which is 1 times the previous value, plus one times the value before -- that. -- -- You can create a series that doubles by having it be just twice the -- previous value: -- --
-- >>> evalAutoN' 10 (autoRegression [2] [1]) () -- [2,,4,8,16,32,64,128,256,512,1024] ---- -- Name comes from the statistical model. autoRegression :: (Num b, Serialize b) => [b] -> [b] -> Auto m a b -- | The non-serializing/non-resuming version of autoRegression. autoRegression_ :: Num b => [b] -> [b] -> Auto m a b -- | A combination of autoRegression and movingAverage. -- Inspired by the statistical model. -- -- Mathematically: -- --
-- y_n = wm_0 * x_(n-0) + wm_1 * x_(n-1) + wm_2 * x_(n-2) + ... -- + wa_1 * y_(n-1) + wa_2 * y_(n-1) + ... ---- -- Where wm_ns are all of the "moving average" weights, where -- the first weight is wm_0, and wa_ns are all of the -- "autoregression" weights, where the first weight is wa_1. arma :: (Num a, Serialize a) => [a] -> [a] -> [a] -> [a] -> Auto m a a -- | The non-serializing/non-resuming version of arma. arma_ :: Num a => [a] -> [a] -> [a] -> [a] -> Auto m a a -- | The output is the running/cumulative mconcat of all of the -- input seen so far, starting with mempty. -- --
-- >>> streamauto' mappender . map Last $ [Just 4, Nothing, Just 2, Just 3] -- [Last (Just 4), Last (Just 4), Last (Just 2), Last (Just 3)] -- -- >>> streamAuto' mappender ["hello","world","good","bye"] -- ["hello","helloworld","helloworldgood","helloworldgoodbye"] ---- --
-- mappender = accum mappend mempty --mappender :: (Serialize a, Monoid a) => Auto m a a -- | The non-resuming/non-serializing version of mappender. mappender_ :: Monoid a => Auto m a a -- | The output is the running <>-sum (mappend for -- Semigroup) of all of the input values so far, starting with a -- given starting value. Basically like mappender, but with a -- starting value. -- --
-- >>> streamAuto' (mappendFrom (Max 0)) [Max 4, Max (-2), Max 3, Max 10] -- [Max 4, Max 4, Max 4, Max 10] ---- --
-- mappendFrom m0 = accum (<>) m0 --mappendFrom :: (Serialize a, Semigroup a) => a -> Auto m a a -- | The non-resuming/non-serializing version of mappender. mappendFrom_ :: Semigroup a => a -> Auto m a a -- | 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. module Control.Auto.Run -- | 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) --throughT :: (Monad m, Traversable t) => Auto m a b -> Auto m (t a) (t b) -- | 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 :: Monad m => Auto m a b -> [a] -> m [b] -- | 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] --streamAuto' :: Auto' a b -> [a] -> [b] -- | 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 :: Monad m => Auto m a b -> [a] -> m ([b], Auto m a b) -- | 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'. overList' :: Auto' a b -> [a] -> ([b], Auto' a b) -- | 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 -> t a -> m (t b, Auto m a b) -- | Like overList', but "streams" the Auto' over all -- elements of any Traversable', returning the final updated -- Auto'. overTraversable' :: Traversable t => Auto' a b -> t a -> (t b, Auto' a b) -- | 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 :: Monad m => Int -> Auto m a b -> a -> m ([b], Auto m a b) -- | 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] --stepAutoN' :: Int -> Auto' a b -> a -> ([b], Auto' a b) -- | 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 :: Monad m => Int -> Auto m a b -> a -> m [b] -- | 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] --evalAutoN' :: Int -> Auto' a b -> a -> [b] -- | 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. interactAuto :: Interval' String String -> IO (Interval' String String) -- | 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. interactRS :: (Read a, Show b) => Interval' a b -> IO (Interval' String String) -- | 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. interactM :: Monad m => (forall c. m c -> IO c) -> (b -> IO Bool) -> Auto m String b -> IO (Auto m String 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. duringRead :: (Monad m, Read a) => Auto m a b -> Interval m String 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. bindRead :: (Monad m, Read a) => Interval m a b -> Interval m String b -- | 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). run :: Monad m => m a -> (b -> m (Maybe a)) -> Auto m a b -> m (Auto m a b) -- | 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. runM :: (Monad m, Monad m') => (forall c. m' c -> m c) -> m a -> (b -> m (Maybe a)) -> Auto m' a b -> m (Auto m' a b) -- | 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. runOnChan :: (b -> IO Bool) -> Chan a -> Auto' a b -> IO (Auto' a b) -- | 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. runOnChanM :: Monad m => (forall c. m c -> IO c) -> (b -> IO Bool) -> Chan a -> Auto m a b -> IO (Auto m a b) -- | 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. streamAutoEffects :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> [a] -> Auto m' a b -> t m b -- | 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. toEffectStream :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> m a -> Auto m' a b -> t m b -- | This module provides tools for working with the automatically derived -- serializability and resumability of Autos. The first half -- contains boring wrappers around encoding and decoding to and from -- binary, filepaths on disk, etc. -- -- The second half contains Auto transformers that "imbue" an -- Auto with IO serialization abilities. Note that these all -- require an underlying Monad that is an instance of -- MonadIO. -- -- You have "identity-like" transformers that take an Auto and -- spit it back out operationally unchanged...but every step, it might do -- some behind-the-scenes saving or re-load itself from disk when it is -- first stepped. Or you have some "trigger enhancers" that take normal -- Autos and give you the ability to "trigger" saving and loading -- events on the Auto using the Blip mechanisms and blip -- stream semantics from Control.Auto.Blip. -- -- Note that the entire Auto construct is a little bit awkward -- when it comes to performing IO effects --- it isn't exactly what they -- were designed for originally. Hooking on effects to stepping can be -- powerful, but as of now, not much has been looked into meaningful -- error handling when working with IO. If you have any experience with -- this and are willing to help, please feel free to send me an e-mail or -- open an issue on the issue tracker! module Control.Auto.Serialize -- | Returns a Put --- instructions (from Data.Serialize) on -- how to "freeze" the Auto, with its internal state, and save it -- to a binary encoding. It can later be reloaded and "resumed" by -- 'resumeAuto'/'decodeAuto'. saveAuto :: Auto m a b -> Put -- | Returns a Get from an Auto --- instructions (from -- Data.Serialize) on taking a ByteString and "restoring" the -- originally saved Auto, in the originally saved state. resumeAuto :: Auto m a b -> Get (Auto m a b) -- | Encode an Auto and its internal state into a ByteString. encodeAuto :: Auto m a b -> ByteString -- | Resume an Auto from its ByteString serialization, -- giving a Left if the deserialization is not possible. decodeAuto :: Auto m a b -> ByteString -> Either String (Auto m a b) -- | Given a FilePath and an Auto, serialize and freeze the -- state of the Auto as binary to that FilePath. writeAuto :: FilePath -> Auto m a b -> IO () -- | Give a FilePath and an Auto, and readAuto will -- attempt to resume the saved state of the Auto from disk, -- reading from the given FilePath. Will return Left upon a -- decoding error, with the error, and Right if the decoding is -- succesful. readAuto :: FilePath -> Auto m a b -> IO (Either String (Auto m a b)) -- | Like readAuto, but will return the original Auto -- (instead of a resumed one) if the file does not exist. -- -- Useful if you want to "resume an Auto" "if there is" a save -- state, or just use it as-is if there isn't. readAutoDef :: FilePath -> Auto m a b -> IO (Either String (Auto m a b)) -- | Transforms the given Auto into an Auto that -- constantly saves its state to the given FilePath at every -- "step". Requires an underlying MonadIO. -- -- Note that (unless the Auto depends on IO), the resulting -- Auto is meant to be operationally identical in its -- inputs/outputs to the original one. saving :: MonadIO m => FilePath -> Auto m a b -> Auto m a b -- | Like loading, except silently suppresses all I/O and decoding -- errors; if there are errors, it returns back the given Auto -- as-is. -- -- Useful for when you aren't sure the save state is on disk or not yet, -- and want to resume it only in the case that it is. loading' :: MonadIO m => FilePath -> Auto m a b -> Auto m a b -- | Transforms the given Auto into an Auto that, when -- you first try to run or step it, "loads" itself from disk at -- the given FilePath. -- -- Will throw a runtime exception on either an I/O error or a decoding -- error. -- -- Note that (unless the Auto depends on IO), the resulting -- Auto is meant to be operationally identical in its -- inputs/outputs to the fast-forwarded original Auto. loading :: MonadIO m => FilePath -> Auto m a b -> Auto m a b -- | Like serializing, except suppresses all I/O and decoding -- errors. -- -- Useful in the case that when the Auto is first run and there is -- no save state yet on disk (or the save state is corrupted), it'll -- "start a new one"; if there is one, it'll load it automatically. Then, -- on every further step in both cases, it'll update the save state. serializing' :: MonadIO m => FilePath -> Auto m a b -> Auto m a b -- | A combination of saving and loading. When the -- Auto is first run, it loads the save state from the given -- FilePath and fast forwards it. Then, subsequently, it updates -- the save state on disk on every step. serializing :: MonadIO m => FilePath -> Auto m a b -> Auto m a b -- | Takes an Auto and basically "wraps" it so that you can trigger -- saves with a blip stream. -- -- For example, we can take sumFrom 0: -- --
-- saveOnB (sumFrom 0) :: Auto IO (Int, Blip FilePath) Int ---- -- It'll behave just like sumFrom 0 (with the input you -- pass in the first field of the tuple)...and whenever the blip stream -- (the second field of the input tuple) emits, it'll save the state of -- sumFrom 0 to disk at the given FilePath. -- -- Contrast to saveFromB, where the Auto itself can trigger -- saves; in this one, saves are triggered "externally". -- -- Might be useful in similar situations as saveFromB, except if -- you want to trigger the save externally. saveOnB :: MonadIO m => Auto m a b -> Auto m (a, Blip FilePath) b -- | Like loadOnB, except silently ignores errors. When a load is -- requested, but there is an IO or parse error, the loading is skipped. loadOnB' :: MonadIO m => Auto m a b -> Auto m (a, Blip FilePath) b -- | Takes an Auto and basically "wraps" it so that you can trigger -- loads/resumes from a file with a blip stream. -- -- For example, we can take sumFrom 0: -- --
-- loadOnB (sumFrom 0) :: Auto IO (Int, Blip FilePath) Int ---- -- It'll behave just like sumFrom 0 (with the input you -- pass in the first field of the tiple)...and whenever the blip stream -- (the second field of the input tuple) emits, it'll "reset" and -- "reload" the sumFrom 0 from the FilePath on -- disk. -- -- Will throw a runtime exception if there is an IO error or a parse -- error. -- -- Contrast to loadFromB, where the Auto itself can trigger -- reloads/resets; in this one, the loads are triggered "externally". -- -- Might be useful in similar situations as loadFromB, except if -- you want to trigger the loading externally. loadOnB :: MonadIO m => Auto m a b -> Auto m (a, Blip FilePath) b -- | Takes an Auto that produces a blip stream with a -- FilePath and a value, and turns it into an Auto that, -- outwardly, produces just the value. -- -- Whenever the output blip stream emits, it automatically serializes and -- saves the state of the Auto to the emitted FilePath. -- -- In practice, this allows any Auto to basically control when it -- wants to "save", by providing a blip stream. -- -- The following is an alternative implementation of saving, -- except saving every two steps instead of every step: -- --
-- saving2 fp a = saveFromB (a &&& (every 2 . pure fp)) ---- -- Or, in proc notation: -- --
-- saving2 fp a = saveFromB $ proc x -> do -- y <- a -< x -- b <- every 2 -< fp -- id -< (y, b) ---- -- (Recall that every n is the Auto that emits -- the received value every n steps) -- -- In useful real-world cases, you can have the Auto decide -- whether or not to save itself based on its input. Like, for example, -- when it detects a certain user command, or when the user has reached a -- given location. -- -- The following takes a FilePath and an Auto (a), -- and turns it into an Auto that "saves" whenever a -- crosses over from positive to negative. -- --
-- saveOnNegative fp a = saveFromB $ proc x -> do -- y <- a -< x -- saveNow <- became (< 0) -< y -- id -< (y, fp <$ saveNow) ---- -- Contrast to saveOnB, where the saves are triggered by outside -- input. In this case, the saves are triggered by the Auto to be -- saved itself. saveFromB :: MonadIO m => Auto m a (b, Blip FilePath) -> Auto m a b -- | Like loadFromB, except silently ignores errors. When a load is -- requested, but there is an IO or parse error, the loading is skipped. loadFromB' :: MonadIO m => Auto m a (b, Blip FilePath) -> Auto m a b -- | Takes an Auto that outputs a b and a blip stream of -- FilePaths and returns an Auto that ouputs only that -- b stream...but every time the blip stream emits, it -- "resets/loads" itself from that FilePath. -- -- The following is a re-implementation of loading...except -- delayed by one (the second step that is run is the first "resumed" -- step). -- --
-- loading2 fp a = loadFromB $ proc x -> do -- y <- a -< x -- loadNow <- immediately -< fp -- id -< (y, loadNow) ---- -- (the blip stream emits only once, immediately, to re-load). -- -- In the real world, you could have the Auto decide to reset or -- resume itself based on a user command: -- --
-- loadFrom = loadFromB $ proc x -> do
-- steps <- count -< ()
-- toLoad <- case words x of
-- ("load":fp:_) -> do
-- immediately -< fp
-- _ -> do
-- never -< ()
-- id -< (steps, toLoad)
--
--
-- This will throw a runtime error on an IO exception or parsing error.
loadFromB :: MonadIO m => Auto m a (b, Blip FilePath) -> Auto m a b
-- | This module contains various Auto transformers for manipulating
-- the flow of time/stepping rate of an Auto.
--
-- Many of these are Auto "transformers", meaning that they take
-- in an Auto and return a transformed Auto, with new
-- stepping behavior.
--
-- For example, there is accelerate:
--
-- -- accelerate :: Monad m => Int -> Auto m a b -> Auto m a [b] ---- -- accelerate n turns an Auto into an Auto -- that "steps itself" n times for every single input/step. The -- result is a list of the results of each single step. -- -- There are also various Autos for observing the passage of time -- (count) and actiong as a "delay" or a way to access the -- previously stepped values of an Auto. module Control.Auto.Time -- | A simple Auto that ignores all input; its output stream counts -- upwards from zero. -- --
-- >>> take 10 . streamAuto' count $ repeat () -- [0,1,2,3,4,5,6,7,8,9] --count :: (Serialize b, Num b) => Auto m a b -- | A non-resuming/non-serializing version of count. count_ :: Num b => Auto m a b -- | When first asked for output, "primes" the Auto first by -- streaming it with all of the given inputs first before processing the -- first input. Aterwards, behaves like normal. -- --
-- >>> streamAuto' (priming [1,2,3] (sumFrom 0)) [1..10] -- [7,9,12,16,21,27,34,42,51,61] ---- -- The Auto behaves as if it had already "processed" the -- [1,2,3], resulting in an accumulator of 6, before it starts -- taking in any input. -- -- Normally this would be silly with an Auto', because the above -- is the same as: -- --
-- >>> let (_, a) = overList' (sumFrom 0) [1,2,3] -- -- >>> streamAuto' a [1..10] -- [7,9,12,16,21,27,34,42,51,61] ---- -- This becomes somewhat more useful when you have "monadic" -- Autos, and want to defer the execution until during normal -- stepping: -- --
-- >>> _ <- streamAuto (priming [1,2,3] (arrM print)) [10,11,12] -- 1 -- IO effects -- 2 -- 3 -- 10 -- 11 -- 12 --priming :: Monad m => [a] -> Auto m a b -> Auto m a b -- | An Auto that returns the last value received by it. Given an -- "initial value" to output first. -- -- From the signal processing world, this is known as the "lag operator" -- L. -- -- This is (potentially) a very dangerous Auto, because its -- usage and its very existence opens the door to breaking -- denotative/declarative style and devolving into imperative style -- coding. However, when used where it is supposed to be used, it is more -- or less invaluable, and will be an essential part of many programs. -- -- Its main usage is for dealing with recursive bindings. If you ever are -- laying out recursive bindings in a high-level/denotative way, you need -- to have at least one value be able to have a "initial output" without -- depending on anything else. lastVal and delay allow you -- to do this. -- -- See the recursive example for more information on the -- appropriate usage of lastVal and delay. -- --
-- >>> streamAuto' (lastVal 100) [1..10] -- [100,1,2,3,4,5,6,7,8,9] --lastVal :: Serialize a => a -> Auto m a a -- | The non-resuming/non-serializing version of lastVal. lastVal_ :: a -> Auto m a a -- | Like arr, but applies the function to the previous value -- of the input, instead of the current value. Used for the same purposes -- as lastVal: to manage recursive bindings. -- -- Warning: Don't use this to do imperative programming! -- --
-- arrD id == lastVal ---- --
-- >>> streamAuto' (arrD negate 100) [1..10] -- [100,-1,-2,-3,-4,-5,-6,-7,-8,-9] --arrD :: Serialize b => (a -> b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of arrD. arrD_ :: Serialize b => (a -> b) -> b -> Auto m a b -- | An alias for lastVal; used in contexts where "delay" is more a -- meaningful description than "last value". All of the warnings for -- lastVal still apply, so you should probably read it if you -- haven't :) delay :: Serialize a => a -> Auto m a a -- | The non-resuming/non-serializing version of delay. delay_ :: a -> Auto m a a -- | Like delay, except has as many "initial values" as the input -- list. Outputs every item in the input list in order before returning -- the first received value. -- --
-- delayList [y0] = delay y0 ---- --
-- >>> streamAuto' (delayList [3,5,7,11]) [1..10] -- [3,5,7,11,1,2,3,4,5,6] --delayList :: (Serialize a, Monad m) => [a] -> Auto m a a -- | The non-resuming/non-serializing version of delayList. delayList_ :: Monad m => [a] -> Auto m a a -- | Like delay, except delays the desired number of steps with the -- same initial output value. -- --
-- delayN n x0 = delayList (replicate n x0) ---- --
-- delayN 1 x0 = delay x0 ---- --
-- >>> streamAuto' (delayN 3 0) [1..10] -- [0,0,0,1,2,3,4,5,6,7] --delayN :: (Serialize a, Monad m) => Int -> a -> Auto m a a -- | The non-resuming/non-serializing version of delayN delayN_ :: Monad m => Int -> a -> Auto m a a -- | "stretch" an Auto out, slowing time. stretch n -- a will take one input, repeat the same output n times -- (ignoring input), and then take another. It ignores all inputs in -- between. -- --
-- >>> let a = stretch 2 (sumFrom 0) -- -- >>> streamAuto' a [1,8,5,4,3,7,2,0] -- [1,1,6,6,9,9,11,11] -- -- [1,_,5,_,3,_,2 ,_ ] <-- the inputs --stretch :: (Serialize b, Monad m) => Int -> Auto m a b -> Auto m a b -- | The non-resuming/non-serializing version of stretch. stretch_ :: Monad m => Int -> Auto m a b -> Auto m a b -- | Like stretch, but instead of holding the the "stretched" -- outputs, outputs a blip stream that emits every time the stretched -- Auto "progresses" (every n ticks) -- -- See stretch for more information. -- --
-- >>> let a = stretchB 2 (accum (+) 0) -- -- >>> streamAuto' a [1,8,5,4,3,7,2,0] -- [Blip 1, NoBlip, Blip 6, NoBlip, Blip 9, NoBlip, Blip 11, NoBlip] --stretchB :: Monad m => Int -> Auto m a b -> Auto m a (Blip b) -- | A more general version of stretch; instead of just ignoring and -- dropping the "stretched/skipped intervals", accumulate all of them up -- with the given accumulating function and then "step" them all at once -- on every nth tick. Also, stead of returning exactly the same -- output every time over the stretched interval, output a function of -- the original output during the stretched intervals. -- --
-- >>> streamAuto' (sumFrom 0) [1..10] -- [1, 3, 6, 10, 15, 21, 28, 36, 45 ,55] -- -- >>> streamAuto' (stretchAccumBy (+) negate 4 (sumFrom 0)) [1..10] -- [1,-1,-1, -1, 15,-15,-15,-15, 45,-45] ---- -- Here, instead of feeding in a number every step, it "accumulates" all -- of the inputs using + and "blasts them into" -- sumFrom 0 every 4 steps. In between the blasts, it -- outputs the negated last seen result. -- -- You can recover the behavior of stretch with -- stretchAccumBy (flip const) id. stretchAccumBy :: (Serialize a, Serialize b, Monad m) => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b -- | The non-serialized/non-resuming version of stretchAccumBy. stretchAccumBy_ :: Monad m => (a -> a -> a) -> (b -> b) -> Int -> Auto m a b -> Auto m a b -- | accelerate n a turns an Auto a into an -- "accelerated" Auto, where every input is fed into the -- Auto n times. All of the results are collected in the -- output. -- -- The same input is fed repeatedly n times. -- --
-- >>> streamAuto' (accelerate 3 (sumFrom 0)) [2,3,4] -- [[2,4,6],[9,12,15],[19,23,27]] -- -- ^adding 2s ^adding 3s ^adding 4s --accelerate :: Monad m => Int -> Auto m a b -> Auto m a [b] -- | accelerateWith xd n a is like accelerate n -- a, except instead of feeding in the input n times, it -- feeds the input in once and repeats the "filler" xd for the -- rest of the accelerating period. -- --
-- >>> streamAuto' (accelerateWith (-1) 3 (sumFrom 0)) [1,10,100] -- [[1,0,-1],[9,8,7],[107,106,105]] -- -- ^ feed in 1 once and -1 twice -- -- ^ feed in 10 once and -1 twice -- -- ^ feed in 100 once and -1 twice --accelerateWith :: Monad m => a -> Int -> Auto m a b -> Auto m a [b] -- | Accelerates the Auto, so instead of taking an a -- and returning a b, it takes a list of a, "streams" -- the Auto over each one, and returns a list of b -- results. -- -- For example, if you normally feed sumFrom 0 a 1, -- then a 2, then a 3, you'd get a 1, then a 3, then a 6. But if you feed -- accelOverList (sumFrom 0) a [1,2], -- you'd get a [1,3], and if you fed it a [3] after, -- you'd get a [6]. -- -- Turns a [a] -> [b] into an [[a]] -> [[b]]; if -- you "chunk up" the input stream as into chunks of input to -- feed all at once, the outputs b will be chunked up the same -- way. -- --
-- >>> streamAuto' (sumFrom 0) [1,2,3,4,5,6,7,8] -- [1,3,6,10,15,21,28,36] -- -- >>> streamAuto' (accelOverList (sumFrom 0)) [[1,2],[],[3,4,5],[6],[7,8]] -- [[1,3],[],[6,10,15],[21],[28,36]] ---- -- Mostly useful if you want to feed an Auto multiple inputs in -- the same step. Note that if you always feed in singleton lists (lists -- with one item), you'll more or less get the same behavior as normal. accelOverList :: Monad m => Auto m a b -> Auto m [a] [b] -- | Takes an Auto that produces (b, Blip c), and -- turns it into an Auto that produces ([b], c). -- -- Basically, the new Auto "squishes together" the periods of -- output between each time the blip stream emits. All outputs between -- each emitted value are accumulated and returned in the resulting -- [b]. -- -- It "does this" in the same manner as accelerateWith and -- fastForward: first feed the input, then step repeatedly with -- the default input value. -- --
-- >>> let a :: Auto' Int (Int, Blip String) -- a = proc i -> do -- sums <- sumFrom 0 -< i -- blp <- every 3 -< i -- emits every 3 ticks. -- id -< (sums, sums <& blp) -- replace emitted value -- -- with the running sum -- -- >>> let skipA :: Auto' Int ([Int], String) -- skipA = skipTo (-1) a -- -- >>> let (res1, skipA') = stepAuto' skipA 8 -- -- >>> res1 -- ([8,7,6], 6) -- fed 8 first, then (-1) repeatedly -- -- >>> let (res2, _ ) = evalAuto skipA' 5 -- -- >>> res2 -- ([11,10,9], 9) -- fed 5 first, then (-1) repeatedly ---- -- If the blip stream never emits then stepping this and getting the -- result or the next/updated Auto never terminates...so watch -- out! skipTo :: Monad m => a -> Auto m a (b, Blip c) -> Auto m a ([b], c) -- | Turns an Interval m a b into an Auto m a -- b --- that is, an Auto m a (Maybe b) into an -- Auto m a b. -- -- It does this by "skipping over" all "off"/Nothing input. When -- the result "should" be a Nothing, it re-runs the -- Interval over and over again with the given default input until -- the Auto turns back "on" again (outputs a Just). -- -- If the Interval reaches a point where it will never be "on" -- again, stepping this and getting the result or the next/updated -- Auto won't terminate...so watch out! -- --
-- >>> let a1 = offFor 3 . sumFrom 0 -- -- >>> streamAuto' a1 [1..10] -- [Nothing, Nothing, Nothing, Just 10, Just 15, Just 21] -- -- >>> streamAuto' (fastForward 0 a1) [1..6] -- [1,3,6,10,15,21] -- -- >>> streamAuto' (fastForward (-10) a1) [1..6] -- [-29,-27,-24,-20,-15,-9] ---- -- In that last example, the first input is 1, then it inputs (-10) until -- it is "on"/Just again (on the fourth step). Then continues -- imputing 2, 3, 4 etc. fastForward :: Monad m => a -> Interval m a b -> Auto m a b -- | Same behavior as fastForward, except accumulates all of the -- Left c outputs in a list. fastForwardEither :: Monad m => a -> Auto m a (Either c b) -> Auto m a (b, [c]) -- | The Autos in this module are all dedicated to managing and -- working with (possibly dynamic) "collections" of Autos: an -- Auto where the output stream is typically many output -- streams collected from running many input streams through many -- internal Autos. -- -- Particularly useful because a lot of these allow you to add or take -- away these "channels of inputs" (or "internal Autos") -- dynamically; so, useful for collections that can be added to or -- deleted from, like monsters on a map. -- -- These multiplex, merge, or collect input streams through many -- Autos and output the multiplexed, merged, or collected output -- streams. -- -- A lot of these Autos take advantaage Interval semantics -- (Maybe for continuous on/off periods) to signal when they want -- to be removed or turned off. -- -- For these, the best way to learn them is probably by seeing examples. -- -- If there is a time when you might want collections of things that can -- be added to or removed from dynamically, this might be what you are -- looking for. -- -- These collections are indispensible for coding real applications; many -- examples of them in use are available in the auto-examples -- project! See those projects for "real-world" guides. module Control.Auto.Collection -- | Give a list of Auto m a b and get back an -- Auto m [a] [b] --- take a list of a's and -- feed them to each of the Autos, and collects their output -- b's. -- -- If the input list doesn't have enough items to give to all of the -- Autos wrapped, then use the given default value. Any extra -- items in the input list are ignored. -- -- For an example, we're going to make a list of Autos that output -- a running sum of all of their inputs, but each starting at a different -- beginning value: -- --
-- summerList :: [Auto' Int Int] -- summerList = [sumFrom 0, sumFrom 10, sumFrom 20, sumFrom 30] ---- -- Then, let's throw it into zipAuto with a sensible default -- value, 0: -- --
-- summings0 :: Auto' [Int] [Int] -- summings0 = zipAuto 0 summerList ---- -- Now let's try it out! -- --
-- >>> let (r1, summings1) = stepAuto' summings0 [1,2,3,4] -- -- >>> r1 -- [ 1, 12, 23, 34] -- -- >>> let (r2, summings2) = stepAuto' summings1 [5,5] -- -- >>> r2 -- [ 6, 17, 23, 34] -- -- >>> let (r3, _ ) = stepAuto' summings2 [10,1,10,1,10000] -- -- >>> r3 -- [16, 18, 33, 35] --zipAuto :: Monad m => a -> [Auto m a b] -> Auto m [a] [b] -- | Like zipAuto, but delay the input by one step. The first input -- to all of them is the "default" value, and after that, feeds in the -- input streams delayed by one. -- -- Let's try the example from zipAuto, except with dZipAuto -- instead: -- --
-- summerList :: [Auto' Int Int] -- summerList = map sumFrom [0, 10, 20, 30] -- -- summings0 :: Auto' [Int] [Int] -- summings0 = dZipAuto 0 summerList ---- -- Trying it out: -- --
-- >>> let (r1, summings1) = stepAuto' summings0 [1,2,3,4] -- -- >>> r1 -- [ 0, 10, 20, 30] -- -- >>> let (r2, summings2) = stepAuto' summings1 [5,5] -- -- >>> r2 -- [ 1, 12, 23, 34] -- -- >>> let (r3, summings3) = stepAuto' summings2 [10,1,10,1,10000] -- -- >>> r3 -- [ 6, 17, 23, 34] -- -- >>> let (r4, _ ) = stepAuto' summings3 [100,100,100,100] -- -- >>> r4 -- [16, 18, 33, 35] --dZipAuto :: (Serialize a, Monad m) => a -> [Auto m a b] -> Auto m [a] [b] -- | The non-serializing/non-resuming version of dZipAuto. dZipAuto_ :: Monad m => a -> [Auto m a b] -> Auto m [a] [b] -- | Takes a bunch of Autos that take streams streams, and turns -- them into one Auto that takes a bunch of blip streams and feeds -- them into each of the original Autos, in order. -- -- It's basically like zipAuto, except instead of taking in normal -- streams of values, it takes in blip streams of values. -- -- If the input streams ever number less than the number of Autos -- zipped, the other Autos are stepped assuming no emitted value. zipAutoB :: Monad m => [Auto m (Blip a) b] -> Auto m [Blip a] [b] -- | A delayed version of zipAutoB dZipAutoB :: (Serialize a, Monad m) => [Auto m (Blip a) b] -> Auto m [Blip a] [b] -- | The non-serializing/non-resuming version of dZipAutoB. dZipAutoB_ :: Monad m => [Auto m (Blip a) b] -> Auto m [Blip a] [b] -- | A dynamic box of Intervals. Takes a list of inputs to feed to -- each one, in the order that they were added. Also takes a blip stream, -- which emits with new Intervals to add to the box. -- -- Add new Intervals to the box however you want with the blip -- stream. -- -- As soon as an Interval turns "off", the Interval is -- removed from the box, and its output is silenced. -- -- The adding/removing aside, the routing of the inputs (the first field -- of the tuple) to the internal Autos and the outputs behaves the -- same as with zipAuto. -- -- This will be a pretty powerful collection if you ever imagine adding -- and destroying behaviors dynamically...like spawning new enemies, or -- something like that. -- -- Let's see an example...here we are going to be throwing a bunch of -- Autos that count to five and then die into our -- dynZip_...once every other step. -- --
-- -- count upwards, then die when you reach 5 -- countThenDie :: Interval' () Int -- countThenDie = onFor 5 . iterator (+1) 1 -- -- -- emit a new countThenDie every two steps -- throwCounters :: Auto' () (Blip [Interval' () Int]) -- throwCounters = tagBlips [countThenDie] . every 2 -- -- a :: Auto' () [Int] -- a = proc _ -> do -- newCounter <- throwCounters -< () -- dynZip_ () -< (repeat (), newCounter) ---- --
-- >>> let (res, _) = stepAutoN' 15 a () -- -- >>> res -- [[], [1 ] -- , [2, ] -- , [3, 1 ] -- , [4, 2 ] -- , [5, 3, 1 ] -- , [ 4, 2 ] -- , [ 5, 3, 1 ] -- , [ 4, 2 ] -- , [ 5, 3, 1] -- ] ---- -- This is a little unweildy, because Autos maybe disappearing out -- of the thing while you are trying to feed inputs into it. You might be -- feeding an input to an Auto...but one of the Autos -- before it on the list has disappeared, so it accidentally goes to the -- wrong one. -- -- Because of this, it is suggested that you use dynMap_, which -- allows you to "target" labeled Autos with your inputs. -- -- This Auto is inherently unserializable, but you can use -- dynZipF for more or less the same functionality, with -- serialization possible. It's only slightly less powerful...for all -- intents and purposes, you should be able to use both in the same -- situations. All of the examples here can be also done with -- dynZipF. dynZip_ :: Monad m => a -> Auto m ([a], Blip [Interval m a b]) [b] -- | Like dynZip_, but instead of taking in a blip stream of -- Intervals directly, takes in a blip stream of ks to -- trigger adding more Intervals to the "box", using the given -- k -> Interval m a b function to make the new -- Interval to add. -- -- Pretty much all of the power of dynZip_, but with -- serialization. -- -- See dynZip_ for examples and caveats. -- -- You could theoretically recover the behavior of dynZip_ with -- dynZipF id, if there wasn't a Serialize -- constraint on the k. dynZipF :: (Serialize k, Monad m) => (k -> Interval m a b) -> a -> Auto m ([a], Blip [k]) [b] -- | The non-serializing/non-resuming version of dynZipF. Well, you -- really might as well use dynZip_, which is more powerful...but -- maybe using this can inspire more disciplined usage. Also works as a -- drop-in replacement for dynZipF. dynZipF_ :: Monad m => (k -> Interval m a b) -> a -> Auto m ([a], Blip [k]) [b] -- | A dynamic box of Autos, indexed by an Int. Takes an -- IntMap of inputs to feed into their corresponding Autos, -- and collect all of the outputs into an output IntMap. -- -- Whenever any of the internal Autos return Nothing, they -- are removed from the collection. -- --
-- >>> import qualified Data.IntMap as IM -- -- >>> let dm0 :: Auto' (IM.IntMap Int) (IM.IntMap Int) -- dm0 = proc x -> do -- initials <- immediately -< [ Just <$> sumFrom 0 -- , Just <$> sumFrom 10 ] -- newIs <- every 3 -< [ Just <$> sumFrom 0 ] -- dynMap_ (-1) -< (x, initials `mergeL` newIs) -- -- >>> let (res1, dm1) = stepAuto' dm0 mempty -- -- >>> res1 -- fromList [(0, -1), (1, 9)] -- -- >>> let (res2, dm2) = stepAuto' dm1 (IM.fromList [(0,100),(1,50)]) -- -- >>> res2 -- fromList [(0, 99), (1, 59)] -- -- >>> let (res3, dm3) = stepAuto' dm2 (IM.fromList [(0,10),(1,5)]) -- -- >>> res3 -- fromList [(0, 109), (1, 64), (2, -1)] -- -- >>> let (res4, _ ) = stepAuto' dm3 (IM.fromList [(1,5),(2,5)]) -- -- >>> res4 -- fromList [(0, 108), (1, 69), (2, 4)] ---- -- One quirk is that every internal Auto is "stepped" at every -- step with the default input; gatherMany is a version of this -- where Autos that do not have a corresponding "input" are left -- unstepped, and their last output preserved in the aggregate output. As -- such, gatherMany might be seen more often. -- -- This Auto is inherently unserializable, but you can use -- dynMapF for more or less the same functionality, with -- serialization possible. It's only slightly less powerful...for all -- intents and purposes, you should be able to use both in the same -- situations. All of the examples here can be also done with -- dynMapF. dynMap_ :: Monad m => a -> Auto m (IntMap a, Blip [Interval m a b]) (IntMap b) -- | Like dynMap_, but instead of taking in a blip stream of -- Intervals directly, takes in a blip stream of ks to -- trigger adding more Intervals to the "box", using the given -- k -> Interval m a b function to make the new -- Interval to add. -- -- Pretty much all of the power of dynMap_, but with -- serialization. -- -- See dynMap_ for examples and use cases. -- -- You could theoretically recover the behavior of dynMap_ with -- dynMapF id, if there wasn't a Serialize -- constraint on the k. dynMapF :: (Serialize k, Monad m) => (k -> Interval m a b) -> a -> Auto m (IntMap a, Blip [k]) (IntMap b) -- | The non-serializing/non-resuming version of dynMapF. Well, you -- really might as well use dynMap_, which is more powerful...but -- maybe using this can inspire more disciplined usage. Also works as a -- drop-in replacement for dynMapF. dynMapF_ :: Monad m => (k -> Interval m a b) -> a -> Auto m (IntMap a, Blip [k]) (IntMap b) -- | Auto multiplexer. Stores a bunch of internal Autos -- indexed by a key. At every step, takes a key-input pair, feeds the -- input to the Auto stored at that key and outputs the output. -- -- If the key given does not yet have an Auto stored at that key, -- initializes a new Auto at that key by using the supplied -- function. -- -- Once initialized, these Autos are stored there forever. -- -- You can play around with some combinators from -- Control.Auto.Switch; for example, with resetOn, you -- can make Autos that "reset" themselves when given a certain -- input. switchOnF could be put to use here too in neat ways. -- --
-- >>> let mx0 = mux (\_ -> sumFrom 0)
--
-- >>> let (res1, mx1) = stepAuto' mx0 ("hello", 5)
--
-- >>> res1
-- 5
--
-- >>> let (res2, mx2) = stepAuto' mx1 ("world", 3)
--
-- >>> res2
-- 3
--
-- >>> let (res3, mx3) = stepAuto' mx2 ("hello", 4)
--
-- >>> res3
-- 9
--
-- >>> streamAuto' mx3 [("world", 2), ("foo", 6), ("foo", 1), ("hello", 2)]
-- [5, 6, 7, 11]
--
mux :: (Serialize k, Ord k, Monad m) => (k -> Auto m a b) -> Auto m (k, a) b
-- | The non-serializing/non-resuming version of mux.
mux_ :: (Ord k, Monad m) => (k -> Auto m a b) -> Auto m (k, a) b
-- | Like muxI, but holds Intervals 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) -> Auto m (k, a) (Maybe b)
-- | The non-serializing/non-resuming version of muxI.
muxI_ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (k, a) (Maybe b)
-- | Auto multiplexer, like mux, except allows update/access
-- of many Autos at a time. Instead of taking in a single
-- key-value pair and outputting a single result, takes in an entire
-- Map of key-value pairs and outputs a Map of key-result
-- pairs.
--
--
-- >>> import qualified Data.Map as M
--
-- >>> let mx0 = mux (\_ -> sumFrom 0)
--
-- >>> let (res1, mx1) = stepAuto' mx0 (M.fromList [ ("hello", 5)
-- , ("world", 3) ])
--
-- >>> res1
-- fromList [("hello", 5), ("world", 3)]
--
-- >>> let (res2, mx2) = stepAuto' mx1 (M.fromList [ ("hello", 4)
-- , ("foo" , 7) ])
--
-- >>> res2
-- fromList [("foo", 7), ("hello", 9)]
--
-- >>> let (res3, _ ) = mx2 (M.fromList [("world", 3), ("foo", 1)])
--
-- >>> res3
-- fromList [("foo", 8), ("world", 6)]
--
--
-- See mux for more notes.
muxMany :: (Serialize k, Ord k, Monad m) => (k -> Auto m a b) -> Auto m (Map k a) (Map k b)
-- | The non-serializing/non-resuming version of muxMany.
muxMany_ :: (Ord k, Monad m) => (k -> Auto m a b) -> Auto m (Map k a) (Map k b)
-- | Like muxManyI, but holds Intervals instead. When any
-- given Interval turns "off", it's removed from the collection.
-- Only Intervals 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) -> Auto m (Map k a) (Map k b)
-- | The non-serializing/non-resuming version of muxManyI.
muxManyI_ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
-- | Keeps an internal Map of Intervals and, at every step,
-- the output is the last seen output of every Interval, indexed
-- under the proper key.
--
-- At every step, the input is a key-value pair; gather will feed
-- that input value to the Interval under the proper key and
-- update the output map with that new result.
--
-- If the key offered the input is not yet a part of the collection,
-- initializes it with the given function.
--
-- Any Interval that turns "off" (outputs Nothing) from
-- this will be immediately removed from the collection. If something for
-- that key is received again, it will re-initialize it.
--
--
-- >>> let sumUntil :: Interval' Int Int
-- sumUntil = proc x -> do
-- sums <- sumFrom 0 -< x
-- stop <- became (> 10) -< sums
-- before -< (sums, stop)
-- -- (a running sum, "on" until the sum is greater than 10)
--
-- >>> let gt0 = gather (\_ -> sumUntil)
--
-- >>> let (res1, gt1) = stepAuto' gt0 ("hello", 5)
--
-- >>> res1
-- fromList [("hello", 5)]
--
-- >>> let (res2, gt2) = stepAuto' gt1 ("world", 7)
--
-- >>> res2
-- fromList [("hello", 5), ("world", 7)]
--
-- >>> let (res3, gt3) = stepAuto' gt2 ("foo", 4)
--
-- >>> res3
-- fromList [("foo", 4), ("hello", 5), ("world", 7)]
--
-- >>> let (res4, gt4) = stepAuto' gt3 ("world", 8)
--
-- >>> res4
-- fromList [("foo", 4), ("hello", 5)]
--
-- >>> streamAuto' gt4 [("world", 2),("bar", 9),("world", 6),("hello", 11)]
-- [ fromList [("foo", 4), ("hello", 5), ("world", 2)]
-- , fromList [("bar", 9), ("foo", 4), ("hello", 5), ("world", 2)]
-- , fromList [("bar", 9), ("foo", 4), ("hello", 5), ("world", 8)]
-- , fromList [("bar", 9), ("foo", 4), ("world", 8)]
-- ]
--
--
-- In practice this ends up being a very common collection; see the
-- auto-examples project for many examples!
--
-- Because everything needs a key, you don't have the fancy
-- "auto-generate new keys" feature of dynMap...however, you
-- could always pull a new key from perBlip
-- enumFromA or something.
--
-- Like with mux, combinators from Control.Auto.Switch like
-- resetOn and switchOnF are very useful here!
gather :: (Ord k, Monad m, Serialize k, Serialize b) => (k -> Interval m a b) -> Auto m (k, a) (Map k b)
-- | The non-serializing/non-resuming version of gather:
--
-- Does serialize the actual Autos themselves; the
-- Autos are all serialized and re-loaded/resumed when 'gather_ f'
-- is resumed.
--
-- Does not serialize the "last outputs", so resumed Autos
-- that have not yet been re-run/accessed to get a fresh output are not
-- represented in the output map at first.
gather_ :: (Ord k, Monad m, Serialize k) => (k -> Interval m a b) -> Auto m (k, a) (Map k b)
-- | The non-serializing/non-resuming vervsion of gather:
--
-- Serializes neither the Autos themselves nor the "last outputs"
-- --- essentially, serializes/resumes nothing.
gather__ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (k, a) (Map k b)
-- | Much like gather, except allows you to pass in multiple
-- key-value pairs every step, to update multiple internal Autos.
--
--
-- >>> import qualified Data.Map as M
--
-- >>> let sumUntil :: Interval' Int Int
-- sumUntil = proc x -> do
-- sums <- sumFrom 0 -< x
-- stop <- became (> 10) -< sums
-- before -< (sums, stop)
-- -- (a running sum, "on" until the sum is greater than 10)
--
-- >>> let gm0 = gatherMany (\_ -> sumUntil)
--
-- >>> let (res1, gm1) = stepAuto' gm0 (M.fromList [ ("hello", 5)
-- , ("world", 7)
-- ])
--
-- >>> res1
-- fromList [("hello", 5), ("world", 7)]
--
-- >>> let (res2, gm2) = stepAuto' gm1 (M.fromList [ ("foo", 4)
-- , ("hello", 3)
-- ])
--
-- >>> res2
-- fromList [("foo", 4), ("hello", 8), ("world", 7)]
--
-- >>> let (res3, gm3) = stepAuto' gm2 (M.fromList [ ("world", 8)
-- , ("bar", 9)
-- ])
--
-- >>> res3
-- fromList [("bar", 9), ("foo", 4), ("hello", 8)]
--
-- >>> let (res4, _ ) = stepAuto' gm3 (M.fromList [ ("world", 2)
-- , ("bar", 10)
-- ])
--
-- >>> res4
-- fromList [("foo", 4), ("hello", 8), ("world", 2)]
--
--
-- See gather for more notes.
gatherMany :: (Ord k, Monad m, Serialize k, Serialize b) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
-- | The non-serializing/non-resuming version of gatherMany:
--
-- Does serialize the actual Autos themselves; the
-- Autos are all serialized and re-loaded/resumed when
-- 'gatherMany_ f' is resumed.
--
-- Does not serialize the "last outputs", so resumed Autos
-- that have not yet been re-run/accessed to get a fresh output are not
-- represented in the output map at first.
gatherMany_ :: (Ord k, Monad m, Serialize k) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
-- | The non-serializing/non-resuming vervsion of gatherMany:
--
-- Serializes neither the Autos themselves nor the "last outputs"
-- --- essentially, serializes/resumes nothing.
gatherMany__ :: (Ord k, Monad m) => (k -> Interval m a b) -> Auto m (Map k a) (Map k b)
-- | This module provides tools for generating and manipulating "blip
-- streams". The blip stream abstraction is not fundamental to
-- Auto, but rather, like interval, is a very useful
-- semantic tool for the denotation of many programs, games, simulations,
-- and computations in general that you are likely to write with this
-- library.
module Control.Auto.Blip
-- | When used in the context of an input or output of an Auto, a
-- Blip a represents a stream that occasionally, at
-- "independent" or "discrete" points, emits a value of type a.
--
-- Contrast this to Interval, where things are meant to be "on"
-- or "off" for contiguous chunks at a time; blip streams are "blippy",
-- and Intervals are "chunky".
--
-- It's here mainly because it's a pretty useful abstraction in the
-- context of the many combinators found in various modules of this
-- library. If you think of an Auto m a (Blip b)
-- as producing a "blip stream", then there are various combinators and
-- functions that are specifically designed to manipulate blip streams.
--
-- For the purposes of the semantics of what Blip is supposed to
-- represent, its constructors are hidden. (Almost) all of the various
-- Blip combinators (and its very useful Functor instance)
-- "preserve Blipness" --- one-at-a-time occurrences remain
-- one-at-a-time under all of these combinators, and you should have
-- enough so that direct access to the constructor is not needed.
--
-- If you are creating a framework, library, or backend, you might want
-- to manually create blip stream-producing Autos for your users
-- to access. In this case, you can import the constructors and useful
-- internal (and, of course, semantically unsafe) functions from
-- Control.Auto.Blip.Internal.
data Blip a
-- | Takes an Auto m a b (an Auto that turns
-- incoming as into outputting bs) into an
-- Auto m (Blip a) (Blip b); the original
-- Auto is lifted to only be applied to emitted contents of a blip
-- stream.
--
-- When the stream emits, the original Auto is "stepped" with the
-- emitted value; when it does not, it is paused and frozen until the
-- next emission.
--
-- -- >>> let sums = perBlip (sumFrom 0) -- -- >>> let blps = eachAt 2 [1,5,2] -- -- >>> take 8 . streamAuto' blps $ repeat () -- [NoBlip, Blip 1, NoBlip, Blip 5, NoBlip, Blip 2, NoBlip, NoBlip] -- -- >>> take 8 . streamAuto' (sums . blps) $ repeat () -- [NoBlip, Blip 1, NoBlip, Blip 6, NoBlip, Blip 8, NoBlip, NoBlip] --perBlip :: Monad m => Auto m a b -> Auto m (Blip a) (Blip b) -- | Merge two blip streams together; the result emits with either -- of the two merged streams emit. When both emit at the same time, emit -- the result of applying the given function on the two emitted values. -- -- Note that this might be too strict for some purposes; see -- mergeL and mergeR for lazier alternatives. merge :: (a -> a -> a) -> Blip a -> Blip a -> Blip a -- | Merges two blip streams together into one, which emits either -- of the original blip streams emit. If both emit at the same time, the -- left (first) one is favored. -- -- Lazy on the second stream if the first stream is emitting. -- -- If we discount laziness, this is merge const. mergeL :: Blip a -> Blip a -> Blip a -- | Merges two blip streams together into one, which emits either -- of the original blip streams emit. If both emit at the same time, the -- right (second) one is favored. -- -- Lazy on the first stream if the second stream is emitting. -- -- If we discount laziness, this is merge (flip -- const). mergeR :: Blip a -> Blip a -> Blip a -- | Merge all the blip streams together into one, favoring the first -- emitted value. mergeLs :: [Blip a] -> Blip a -- | Merge all the blip streams together into one, favoring the last -- emitted value. mergeRs :: [Blip a] -> Blip a -- | 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 ---- | Deprecated: Starting in v0.5, will have new functionality. foldrB :: (a -> a -> a) -> a -> [Blip a] -> Blip a -- | 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 ---- | Deprecated: Starting in v0.5, will have new functionality. foldlB' :: (a -> a -> a) -> a -> [Blip a] -> Blip a -- | An Auto that runs every input through a a -> -- Maybe b test and produces a blip stream that emits the -- value inside every Just result. -- -- Particularly useful with prisms from the lens package, where -- things like emitJusts (preview _Right) will emit the -- b whenever the input Either a b stream is a -- Right. -- -- Warning! Carries all of the same dangers of emitOn. You can -- easily break blip semantics with this if you aren't sure what you are -- doing. Remember to only emit at discrete, separate occurences, and not -- for interval-like (on and off for chunks at a time) things. For -- interval semantics, we have Control.Auto.Interval. -- -- See the examples of emitOn for more concrete good/bad use -- cases. emitJusts :: (a -> Maybe b) -> Auto m a (Blip b) -- | 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) -- | An Auto that emits whenever it receives a Just input, -- with the value inside the Just. -- -- Warning! Carries all of the same dangers of emitOn. You can -- easily break blip semantics with this if you aren't sure what you are -- doing. Remember to only emit at discrete, separate occurences, and not -- for interval-like (on and off for chunks at a time) things. For -- interval semantics, we have Control.Auto.Interval. -- -- See the examples of emitOn for more concrete good/bad use -- cases. -- --
-- onJusts == emitJusts id --onJusts :: Auto m (Maybe a) (Blip a) -- | 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. -- --
-- onEithers == emitEithers id --onEithers :: Auto m (Either a b) (Blip a, Blip b) -- | Produces a blip stream that emits the input value whenever the input -- satisfies a given predicate. -- -- Warning! This Auto has the capability of "breaking" blip -- semantics. Be sure you know what you are doing when using this. Blip -- streams are semantically supposed to only emit at discrete, separate -- occurrences. Do not use this for interval-like (on and off for chunks -- at a time) things; each input should be dealt with as a separate -- thing. -- -- For interval semantics, we have Interval from -- Control.Auto.Interval. -- -- Good example: -- --
-- -- is only emitting at discrete blips -- emitOn even . iterator (+ 1) 0 ---- -- Bad examples: -- --
-- -- is emitting for "durations" or "intervals" of time. -- emitOn (< 10) . iterator (+ 1) 0 -- -- emitOn (const True) . foo ---- -- These bad examples would be good use cases of Interval. -- -- Can be particularly useful with prisms from the lens package, -- where things like emitOn (has _Right) and emitOn (hasn't -- _Right) will emit the input Either a b whenever it is or -- isn't a Right. See emitJusts for more common uses with -- lens. emitOn :: (a -> Bool) -> Auto m a (Blip a) -- | 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 -> Auto m (Blip a) a -- | fromBlipsWith d f is an Auto that decomposes -- the incoming blip stream by constantly outputting d except -- when the stream emits, and outputs the result of applying f -- to the emitted value when it does. fromBlipsWith :: b -> (a -> b) -> Auto m (Blip a) b -- | Collapse a blip stream of as into a stream of `Maybe a`'s asMaybes :: Auto m (Blip a) (Maybe a) -- | holdWith y0 is an Auto whose output is always -- the /most recently emitted/ value from the input blip stream. Before -- anything is emitted, y0 is outputted as a placeholder. -- -- Contrast with hold from Control.Auto.Interval. holdWith :: Serialize a => a -> Auto m (Blip a) a -- | A non-serializing/non-resumable version of holdWith. holdWith_ :: a -> Auto m (Blip a) a -- | 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 -- | An Auto that ignores its input and produces a blip stream never -- emits. never :: Auto m a (Blip b) -- | Produces a blip stream that emits with the first received input value, -- and never again after that. -- -- Often used with pure: -- --
-- immediately . pure "Emit me!" ---- -- Or, in proc notation: -- --
-- blp <- immediately -< "Emit me!" ---- -- to get a blip stream that emits a given value (eg., "Emit me!") once -- and stops emitting ever again. -- --
-- >>> streamAuto' (immediately . pure "Emit me!") [1..5] -- [Blip "Emit Me!", NoBlip, NoBlip, NoBlip, NoBlip] --immediately :: Auto m a (Blip a) -- | Produces a blip stream that only emits once, with the input value on -- the given step number. It emits the input on that many steps. -- --
-- immediately == inB 1 --inB :: Int -> Auto m a (Blip a) -- | every n is an Auto that emits with the incoming -- inputs on every nth input value. First emitted value is on -- the nth step. -- -- Will obviously break blip semantics when you pass in 1. every :: Int -> Auto m a (Blip a) -- | eachAt n xs is an Auto that ignores its input -- and creates a blip stream that emits each element of xs one -- at a time, evey n steps. First emitted value is at step -- n. -- -- Once the list is exhausted, never emits again. -- -- Obviously breaks blip semantics when you pass in 1. -- -- The process of serializing and resuming this Auto is O(n) space -- and time with the length of xs. So don't serialize this if -- you plan on passing an infinite list :) See -- Control.Auto.Generate for more options. -- --
-- eachAt n xs == perBlip (fromList xs) . every n --eachAt :: Serialize b => Int -> [b] -> Auto m a (Blip b) -- | The non-serializing/non-resumable version of eachAt. eachAt_ :: Int -> [b] -> Auto m a (Blip b) -- | 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]) -- | The non-serializing/non-resuming version of collectN collectN_ :: Int -> Auto m a (Blip [a]) -- | Re-emits every emission from the input blip stream, but replaces its -- value with the given value. -- --
-- tagBlips x == modifyBlips (const x) --tagBlips :: b -> Auto m (Blip a) (Blip b) -- | Re-emits every emission from the input blip stream, but applies the -- given function to the emitted value. modifyBlips :: (a -> b) -> Auto m (Blip a) (Blip b) -- | Takes two Autos producing blip streams and returns a "merged" -- Auto that emits when either of the original Autos emit. -- When both emit at the same time, the left (first) one is favored. -- --
-- a1 <& a2 == mergeL <$> a1 <*> a2 --(<&) :: Monad m => Auto m a (Blip b) -> Auto m a (Blip b) -> Auto m a (Blip b) -- | Takes two Autos producing blip streams and returns a "merged" -- Auto that emits when either of the original Autos emit. -- When both emit at the same time, the right (second) one is favored. -- --
-- a1 &> a2 == mergeR <$> a1 <*> a2 --(&>) :: Monad m => Auto m a (Blip b) -> Auto m a (Blip b) -> Auto m a (Blip b) -- | Supress all upstream emitted values except for the very first. once :: Auto m (Blip a) (Blip a) -- | Suppress only the first emission coming from upstream, and let all the -- others pass uninhibited. notYet :: Auto m (Blip a) (Blip a) -- | Takes in a blip stream and outputs a blip stream where each emission -- is delayed/lagged by one step. -- --
-- >>> streamAuto' (emitOn (\x -> x `mod` 3 == 0)) [1..9] -- -- >>> [NoBlip, NoBlip, Blip 3, NoBlip, NoBlip, Blip 6, NoBlip, NoBlip, Blip 9] -- -- >>> streamAuto' (lagBlips . emitOn (\x -> x `mod` 3 == 0)) [1..9] -- -- >>> [NoBlip, NoBlip, NoBlip, Blip 3, NoBlip, NoBlip, Blip 6, NoBlip, NoBlip] --lagBlips :: Serialize a => Auto m (Blip a) (Blip a) -- | The non-serializing/non-resuming version of lagBlips. lagBlips_ :: Auto m (Blip a) (Blip a) -- | Suppress all upstream emissions when the predicate (on the emitted -- value) fails. filterB :: (a -> Bool) -> Auto m (Blip a) (Blip a) -- | Splits a blip stream based on a predicate. Takes in one blip -- stream and produces two: the first emits whenever the input emits with -- a value that passes the predicate, and the second emits whenever the -- input emits with a value that doesn't. splitB :: (a -> Bool) -> Auto m (Blip a) (Blip a, Blip a) -- | 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)) -- | The non-serializing/non-resuming version of collectB. collectB_ :: Auto m (Blip a, Blip b) (Blip (a, b)) -- | 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]) -- | The non-serializing/non-resuming version of collectBs. collectBs_ :: Int -> Auto m (Blip a) (Blip [a]) -- | Collapses a blip stream of blip streams into single blip -- stream. that emits whenever the inner-nested stream emits. joinB :: Auto m (Blip (Blip a)) (Blip a) -- | 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. mapMaybeB :: (a -> Maybe b) -> Auto m (Blip a) (Blip b) -- | takeB n allows only the first n emissions to -- pass; it suppresses all of the rest. takeB :: Int -> Auto m (Blip a) (Blip a) -- | Allow all emitted valuesto pass until the first that fails the -- predicate. takeWhileB :: (a -> Bool) -> Auto m (Blip a) (Blip a) -- | dropB n suppresses the first n emissions from -- upstream and passes through the rest uninhibited. dropB :: Int -> Auto m (Blip a) (Blip a) -- | Suppress all emited values until the first one satisfying the -- predicate, then allow the rest to pass through. dropWhileB :: (a -> Bool) -> Auto m (Blip a) (Blip a) -- | Accumulates all emissions in the incoming blip stream with a "folding -- function", with a given starting value. b -> a -> b, -- with a starting b, gives Auto m (Blip a) -- (Blip b). -- -- The resulting blip stream will emit every time the input stream emits, -- but with the "accumulated value". -- -- Basically accum, but on blip stream emissions. -- --
-- accumB f x0 == perBlip (accum f x0) --accumB :: Serialize b => (b -> a -> b) -> b -> Auto m (Blip a) (Blip b) -- | The non-serializing/non-resuming version of accumB. accumB_ :: (b -> a -> b) -> b -> Auto m (Blip a) (Blip b) -- | The output is the result of folding up every emitted value seen thus -- far, with the given folding function and initial value. -- --
-- scanB f x0 == holdWith x0 . accumB f x0 ---- --
-- >>> let a = scanB (+) 0 . eachAt 2 [1,2,3] -- -- >>> take 8 . streamAuto' a $ repeat () -- [0, 1, 1, 3, 3, 6, 6, 6, 6] --scanB :: Serialize b => (b -> a -> b) -> b -> Auto m (Blip a) b -- | The non-serializing/non-resuming version of scanB. scanB_ :: (b -> a -> b) -> b -> Auto m (Blip a) b -- | The output is the mconcat (monoid sum) of all emitted values -- seen this far. mscanB :: (Monoid a, Serialize a) => Auto m (Blip a) a -- | The non-serializing/non-resuming version of mscanB. mscanB_ :: Monoid a => Auto m (Blip a) a -- | The output is the number of emitted values received from the upstream -- blip stream so far. countB :: Auto m (Blip a) Int -- | Blip stream that emits whenever the input value changes. Emits with -- the new value. -- -- Warning: Note that, when composed on a value that is never expected to -- keep the same value twice, this technically breaks blip semantics. onChange :: (Serialize a, Eq a) => Auto m a (Blip a) -- | The non-serializing/non-resumable version of onChange. onChange_ :: Eq a => Auto m a (Blip a) -- | Blip stream that emits whenever the predicate applied to the input -- switches from false to true. Emits with the triggering input value. became :: Serialize a => (a -> Bool) -> Auto m a (Blip a) -- | The non-serializing/non-resumable version of became. became_ :: (a -> Bool) -> Auto m a (Blip a) -- | Like became, but emits a '()' instead of the triggering input -- value. -- -- Useful because it can be serialized without the output needing a -- Serialize instance. became' :: (a -> Bool) -> Auto m a (Blip ()) -- | Blip stream that emits whenever the predicate applied to the input -- switches from true to false. Emits with the triggering input value. noLonger :: Serialize a => (a -> Bool) -> Auto m a (Blip a) -- | The non-serializing/non-resumable version of noLonger. noLonger_ :: (a -> Bool) -> Auto m a (Blip a) -- | Like noLonger, but emits a '()' instead of the triggering input -- value. -- -- Useful because it can be serialized without the output needing a -- Serialize instance. noLonger' :: (a -> Bool) -> Auto m a (Blip ()) -- | Blip stream that emits whenever the predicate applied to the input -- switches from true to false or false to true. Emits with the -- triggering input value. onFlip :: (Serialize a, Monad m) => (a -> Bool) -> Auto m a (Blip a) -- | The non-serializing/non-resumable version of onFlip. onFlip_ :: Monad m => (a -> Bool) -> Auto m a (Blip a) -- | 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. onFlip' :: Monad m => (a -> Bool) -> Auto m a (Blip Bool) -- | This module exports the preferred ways of interacting with the -- underlying Monad of the Auto type, including accessing, -- executing, and manipulating such effects. module Control.Auto.Effects -- | Applies the given "monadic function" (function returning a monadic -- action) to every incoming item; the result is the result of executing -- the action returned. -- -- Note that this essentially lifts a "Kleisli arrow"; it's like -- arr, but for "monadic functions" instead of normal functions: -- --
-- arr :: (a -> b) -> Auto m a b -- arrM :: (a -> m b) -> Auto m a b ---- --
-- arrM f . arrM g == arrM (f <=< g) ---- -- One neat trick you can do is that you can "tag on effects" to a normal -- Auto by using *> from Control.Applicative. For -- example: -- --
-- >>> let a = arrM print *> sumFrom 0 -- -- >>> ys <- streamAuto a [1..5] -- 1 -- IO output -- 2 -- 3 -- 4 -- 5 -- -- >>> ys -- [1,3,6,10,15] -- the result ---- -- Here, a behaves "just like" sumFrom -- 0...except, when you step it, it prints out to stdout as a -- side-effect. We just gave automatic stdout logging behavior! arrM :: (a -> m b) -> Auto m a b -- | To get every output, executes the monadic action and returns the -- result as the output. Always ignores input. -- -- This is basically like an "effectful" pure: -- --
-- pure :: b -> Auto m a b -- effect :: m b -> Auto m a b ---- -- The output of pure is always the same, and the output of -- effect is always the result of the same monadic action. Both -- ignore their inputs. -- -- Fun times when the underling Monad is, for instance, -- Reader. -- --
-- >>> let a = effect ask :: Auto (Reader b) a b -- -- >>> let r = evalAuto a () :: Reader b b -- -- >>> runReader r "hello" -- "hello" -- -- >>> runReader r 100 -- 100 ---- -- If your underling monad has effects (IO, State, -- Maybe, Writer, etc.), then it might be fun to take -- advantage of *> from Control.Applicative to "tack on" -- an effect to a normal Auto: -- --
-- >>> let a = effect (modify (+1)) *> sumFrom 0 :: Auto (State Int) Int Int -- -- >>> let st = streamAuto a [1..10] -- -- >>> let (ys, s') = runState st 0 -- -- >>> ys -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> s' -- 10 ---- -- Out Auto a behaves exactly like sumFrom -- 0, except at each step, it also increments the underlying/global -- state by one. It is sumFrom 0 with an "attached -- effect". effect :: m b -> Auto m a b -- | The input stream is a stream of monadic actions, and the output stream -- is the result of their executions, through executing them. effects :: Monad m => Auto m (m a) a -- | Maps one blip stream to another; replaces every emitted value with the -- result of the monadic function, executing it to get the result. arrMB :: Monad m => (a -> m b) -> Auto m (Blip a) (Blip b) -- | Maps one blip stream to another; replaces every emitted value with the -- result of a fixed monadic action, run every time an emitted value is -- received. effectB :: Monad m => m b -> Auto m (Blip a) (Blip b) -- | Outputs the identical blip stream that is received; however, every -- time it sees an emitted value, executes the given monadic action on -- the side. execB :: Monad m => m b -> Auto m (Blip a) (Blip a) -- | The very first output executes a monadic action and uses the result as -- the output, ignoring all input. From then on, it persistently outputs -- that first result. -- -- Like execOnce, except outputs the result of the action instead -- of ignoring it. -- -- Useful for loading resources in IO on the "first step", like a word -- list: -- --
-- dictionary :: Auto IO a [String] -- dictionary = cache (lines $ readFile "wordlist.txt") --cache :: (Serialize b, Monad m) => m b -> Auto m a b -- | Always outputs '()', but when asked for the first output, executes the -- given monadic action. -- -- Pretty much like cache, but always outputs '()'. execOnce :: Monad m => m b -> Auto m a () -- | The non-resumable/non-serializable version of cache. Every time -- the Auto is deserialized/reloaded, it re-executes the action to -- retrieve the result again. -- -- Useful in cases where you want to "re-load" an expensive resource on -- every startup, instead of saving it to in the save states. -- --
-- dictionary :: Auto IO a [String] -- dictionary = cache_ (lines $ readFile "dictionary.txt") --cache_ :: Monad m => m b -> Auto m a b -- | The non-resumable/non-serializable version of execOnce. Every -- time the Auto is deserialized/reloaded, the action is -- re-executed again. execOnce_ :: Monad m => m b -> Auto m a () -- | Swaps out the underlying Monad of an Auto using the -- given monad morphism "transforming function", a natural -- transformation. -- -- Basically, given a function to "swap out" any m a with an -- m' a, it swaps out the underlying monad of the Auto. -- -- This forms a functor, so you rest assured in things like this: -- --
-- hoistA id == id -- hoistA f a1 . hoistA f a2 == hoistA f (a1 . a2) --hoistA :: (Monad m, Monad m') => (forall c. m c -> m' c) -> Auto m a b -> Auto m' a b -- | Generalizes an Auto' a b to an Auto m a -- b' for any Monad m, using hoist. -- -- You generally should be able to avoid using this if you never directly -- write any Auto's and always write 'Auto m' parameterized over -- all Monads, but...in case you import one from a library or -- something, you can use this. generalizeA :: Monad m => Auto' a b -> Auto m a b -- | Unrolls the underlying ReaderT of an Auto into an -- Auto that takes in the input "environment" every turn in -- addition to the normal input. -- -- So you can use any ReaderT r m as if it were an -- m. Useful if you want to compose and create some isolated -- Autos with access to an underlying environment, but not your -- entire program. -- -- Also just simply useful as a convenient way to use an Auto over -- Reader with stepAuto and friends. -- -- When used with Reader r, it turns an Auto -- (Reader r) a b into an Auto' (a, r) b. runReaderA :: Monad m => Auto (ReaderT r m) a b -> Auto m (a, r) b -- | 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 "sees" that value when it asks for one. -- --
-- >>> let a = effect ask :: Auto (Reader b) a b -- -- >>> let rdr = streamAuto' a [1..5] :: Reader b [b] -- -- >>> runReader rdr "hey" -- ["hey", "hey", "hey", "hey", "hey"] ---- -- Useful if you wanted to use it inside/composed with an Auto -- that does not have a global environment: -- --
-- bar :: Auto' Int String -- bar = proc x -> do -- hey <- sealReader (effect ask) "hey" -< () -- id -< hey ++ show x ---- --
-- >>> streamAuto' bar [1..5] -- ["hey1", "hey2", "hey3", "hey4", "hey5"] ---- -- Note that this version serializes the given r environment, so -- that every time the Auto is reloaded/resumed, it resumes with -- the originally given r environment, ignoring whatever -- r is given to it when trying to resume it. If this is not the -- behavior you want, use sealReader_. -- -- Reader is convenient because it allows you to "chain" and -- "compose" Autos with a common environment, instead of -- explicitly passing in values every time. For a convenient way of -- generating Autos under ReaderT, and also for some -- motivating examples, see readerA and runReaderA. sealReader :: (Monad m, Serialize r) => Auto (ReaderT r m) a b -> r -> Auto m a b -- | 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 -> r -> Auto m a b -- | Transforms an Auto on two input streams ( a "normal input" -- stream a and an "environment input stream" r) into -- an Auto on one input stream a with an underlying -- environment r through a Reader monad. -- -- Why is this useful? Well, if you have several Autos that all -- take in a side r stream, and you want to convey that every -- single one should get the same r at every step, you -- can instead have all of them pull from a common underlying global -- environment. -- -- Note: Function is the inverse of runReaderA: -- --
-- readerA . runReaderA == id -- runReaderA . readerA == id --readerA :: Monad m => Auto m (a, r) b -> Auto (ReaderT r m) a b -- | 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 -- readablefullhas a value, if it does not. -- -- Basically a disciplined wrapper/usage over sealReaderM. sealReaderMVar :: MonadIO m => Auto (ReaderT r m) a b -> MVar r -> Auto m a b -- | 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 unmaintainble 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 -> m r -> Auto m a b -- | Transforms an Auto on with two output streams (a "normal output -- stream" b, and a "logging output stream" w) into an -- Auto with just one output stream a, funneling the -- logging stream w into an underlying WriterT monad. -- -- Note: Function is the inverse of runWriterA: -- --
-- writerA . runWriterA == id -- runWriterA . writerA == id --writerA :: (Monad m, Monoid w) => Auto m a (b, w) -> Auto (WriterT w m) a b -- | Unrolls the underlying WriterT w m -- Monad, so that an Auto that takes in a stream of -- a and outputs a stream of b will now output a stream -- (b, w), where w is the "new log" of the underlying -- Writer at every step. -- -- Examples: -- --
-- foo :: Auto (Writer (Sum Int)) Int Int -- foo = effect (tell 1) *> effect (tell 1) *> sumFrom 0 ---- --
-- >>> let fooWriter = streamAuto foo -- -- >>> runWriter $ fooWriter [1..10] -- ([1,3,6,10,15,21,28,36,45,55], Sum 20) ---- -- foo increments an underlying counter twice every time it is -- stepped; its "result" is just the cumulative sum of the inputs. -- -- When we "stream" it, we get a [Int] -> Writer (Sum -- Int) [Int]...which we can give an input list and -- runWriter it, getting a list of outputs and a "final -- accumulator state" of 10, for stepping it ten times. -- -- However, if we use runWriterA before streaming it, we get: -- --
-- >>> let fooW = runWriterA foo -- -- >>> streamAuto' fooW [1..10] -- [ (1 , Sum 2), (3 , Sum 2), (6 , Sum 2) -- , (10, Sum 2), (15, Sum 2), (21, Sum 2), -- ... ---- -- Instead of accumulating it between steps, we get to "catch" the -- Writer output at every individual step. -- -- We can write and compose our own Autos under Writer, -- using the convenience of a shared accumulator, and then "use them" -- with other Autos: -- --
-- bar :: Auto' Int Int -- bar = proc x -> do -- (y, w) <- runWriterA foo -< x -- blah <- blah -< w ---- -- And now you have access to the underlying accumulator of foo -- to access. There, w represents the continually updating -- accumulator under foo, and will be different/growing at every -- "step". -- -- For a convenient way to create an Auto under -- WriterT, see writerA. runWriterA :: (Monad m, Monoid w) => Auto (WriterT w m) a b -> Auto m a (b, w) -- | Takes an Auto that works with underlying global, mutable state, -- and "seals off the state" from the outside world. -- -- An 'Auto (StateT s m) a b' maps a stream of a to a stream of -- b, but does so in the context of requiring an initial -- s to start, and outputting a modified s. -- -- Consider this example State Auto: -- --
-- foo :: Auto (State Int) Int Int -- foo = proc x -> do -- execB (modify (+1)) . emitOn odd -< x -- execB (modify (*2)) . emitOn even -< x -- st <- effect get -< () -- sumX <- sumFrom 0 -< x -- id -< sumX + st ---- -- On every output, the "global" state is incremented if the input is odd -- and doubled if the input is even. The stream st is always the -- value of the global state at that point. sumX is the -- cumulative sum of the inputs. The final result is the sum of the value -- of the global state and the cumulative sum. -- -- In writing like this, you lose some of the denotative properties -- because you are working with a global state that updates at every -- output. You have some benefit of now being able to work with global -- state, if that's what you wanted I guess. -- -- To "run" it, you could use streamAuto to get a -- State Int Int: -- --
-- >>> let st = streamAuto foo [1..10] :: State Int Int -- -- >>> runState st 5 -- ([ 7, 15, 19, 36, 42, 75, 83,136,156,277], 222) ---- -- (The starting state is 5 and the ending state after all of that is -- 222) -- -- However, writing your entire program with global state is a bad bad -- idea! So, how can you get the "benefits" of having small parts like -- foo be written using State, and being able to use it -- in a program with no global state? -- -- Using sealState! Write the part of your program that would like -- shared global state with State...and compose it with the rest -- as if it doesn't, locking it away! -- --
-- sealState :: Auto (State s) a b -> s -> Auto' a b -- sealState foo 5 :: Auto' Int Int ---- --
-- bar :: Auto' Int (Int, String) -- bar = proc x -> do -- food <- sealState foo 5 -< x -- id -< (food, show x) ---- --
-- >>> streamAuto' bar [1..10] -- [ (7, "1"), (15, "2"), (19, "3"), (36, "4"), (42, "5"), (75, "6") ... ---- -- We say that sealState f s0 takes an input stream, and -- the output stream is the result of running the stream through -- f, first with an initial state of s0, and afterwards -- with each next updated state. -- -- If you wanted to "seal" the state and have it be untouchable to the -- outside world, yet still have a way to "monitor"/"view" it, you can -- modify the original Auto using &&&, -- effect, and 'get to get a "view" of the state: -- --
-- >>> streamAuto' (sealState (foo &&& effect get) 5) [1..10] -- [(7,6),(15,12),(19,13),(36,26),(42,27),(75,54),(83,55),(146,110),(156,111),(277,222)] ---- -- Now, every output of sealState foo 5 is tuplied up -- with a peek of its state at that point. -- -- For a convenient way of "creating" an Auto under StateT -- in the first place, see stateA. sealState :: (Monad m, Serialize s) => Auto (StateT s m) a b -> s -> Auto m a b -- | The non-resuming/non-serializing version of sealState. sealState_ :: Monad m => Auto (StateT s m) a b -> s -> Auto m a b -- | Unrolls the underlying StateT of an Auto into an -- Auto that takes in an input state every turn (in addition to -- the normal input) and outputs, along with the original result, the -- modified state. -- -- So now you can use any StateT s m as if it were an -- m. Useful if you want to compose and create some isolated -- Autos with access to an underlying state, but not your entire -- program. -- -- Also just simply useful as a convenient way to use an Auto over -- State with stepAuto and friends. -- -- When used with State s, it turns an Auto -- (State s) a b into an Auto' (a, s) (b, -- s). -- -- For a convenient way to "generate" an Auto StateT, see -- stateA runStateA :: Monad m => Auto (StateT s m) a b -> Auto m (a, s) (b, s) -- | Transforms an Auto with two input streams and two output -- streams (a "normal" input a output b stream, and a -- "state transforming" side-stream taking in s and outputting -- s), abstracts away the s stream as a modifcation to -- an underyling StateT monad. That is, your normal inputs and -- outputs are now your only inputs and outputs, and your input -- s comes from the underlying global mutable state, and the -- output s goes to update the underlying global mutable state. -- -- For example, you might have a bunch of Autos that interact with -- a global mutable state: -- --
-- foo :: Auto (StateT Double m) Int Bool -- bar :: Auto (StateT Double m) Bool Int -- baz :: Auto (StateT Double m) Bool String ---- -- Where foo, bar, and baz all interact with -- global mutable state. You'd use them like this: -- --
-- full :: Auto (StateT Double m) Int String -- full = proc inp -> do -- fo <- foo -< inp -- br <- bar -< fo -- bz <- baz -< fo -- id -< replicae br bz ---- -- stateA allows you generate a new Auto under -- StateT: -- --
-- thing :: Auto m (Int, Double) (Bool, Double) -- stateA thing :: Auto (StateT Double m) Int Bool ---- -- So now the two side-channels are interpreted as working with the -- global state: -- --
-- full :: Auto (StateT Double m) Int String -- full = proc inp -> do -- fo <- foo -< inp -- tg <- stateA thing -< inp -- br <- bar -< fo || tg -- bz <- baz -< fo && tg -- id -< replicae br bz ---- -- You can then "seal it all up" in the end with an initial state, that -- keeps on re-running itself with the resulting state every time: -- --
-- full' :: Double -> Auto m Int String -- full' = sealState full ---- -- Admittedly, this is a bit more esoteric and dangerous (programming -- with global state? what?) than its components readerA and -- writerA; I don't actually recommend you programming with global -- state unless it really is the best solution to your problem...it tends -- to encourage imperative code/loops, and "unreasonable" and manageable -- code. See documentation for sealStateA for best practices. -- Basically every bad thing that comes with global mutable state. But, -- this is provided here for sake of completeness with readerA and -- writerA. -- -- Note: function is the inverse of runstateA. -- --
-- stateA . runStateA == id -- runStateA . stateA == id --stateA :: Monad m => Auto m (a, s) (b, s) -> Auto (StateT s m) a b -- | Like stateA, but assumes that the output is the modified state. accumA :: Monad m => Auto m (a, s) s -> Auto (StateT s m) a s -- | Unrolls the underlying Monad of an Auto if it -- happens to be Traversable ('[]', Maybe, etc.). -- -- It can turn, for example, an Auto [] a b into an -- Auto' a [b]; it collects all of the results together. -- Or an Auto Maybe a b into an Auto' a -- (Maybe b). -- -- This might be useful if you want to make some sort of "underlying -- inhibiting" Auto where the entire computation might just end up -- being Nothing in the end. With this, you can turn that -- possibly-catastrophically-failing Auto (with an underlying -- Monad of Maybe) into a normal Auto, and use it as -- a normal Auto in composition with other -- Autos...returning Just if your computation succeeded. -- --
-- runTraversableA :: Auto Maybe a b -> Interval' a b ---- --
-- foo :: Auto Maybe Int Int -- foo = arrM $ x -> if even x then Just (x div 2) else Nothing -- -- bar :: Auto Maybe Int Int -- 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] -- [Just 1, Just 2, Just 3, Just 7] --runTraversableA :: (Monad f, Traversable f) => Auto f a b -> Auto m a (f b) -- | 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 with the Exception. Otherwise, will output -- left. -- -- Note that you have to explicitly specify the type of the exceptions -- you are catching; see Control.Exception documentation for more -- details. catchA :: Exception e => Auto IO a b -> Auto IO a (Either e b) -- | A collection of versatile switching mechanisms. Switching is really a -- core mechanic at the heart of how to structure a lot of program -- logics. Switching from one "mode" to another, from dead to alive, from -- room to room, menu to menu...switching between Autos is a core -- part about how many programs are built. -- -- All of the switches here take advantage of either blip semantics (from -- Control.Auto.Blip) or Interval semantics (from -- Control.Auto.Interval)...so this is where maintaining -- semantically meaningful blip streams and intervals pays off! -- -- Each switch here has various examples, and you'll find many of these -- in use in the example projects. -- -- Note the naming convention going on here (also used in -- Control.Auto.Serialize): A switch "from" a blip stream is -- triggered "internally" by the Auto being switched itself; a -- switch "on" a blip stream is triggered "externally" by an Auto -- that is not swiched. module Control.Auto.Switch -- | "This, then that". Behave like the first Interval (and run its -- effects) as long as it is "on" (outputting Just). As soon as it -- turns off (is Nothing), it'll "switch over" and begin behaving -- like the second Auto forever, running the effects of the second -- Auto, too. Works well if the Autos follow interval -- semantics from Control.Auto.Interval. -- --
-- >>> let a1 = whenI (<= 4) --> pure 0 -- -- >>> streamAuto' a1 [1..10] -- [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] ---- -- (whenI only lets items satisfying the predicate pass through as -- "on", and is "off" otherwise; pure is the Auto that -- always produces the same output) -- -- Association works in a way that you can "chain" -->s, as -- long as you have an appropriate Auto (and not Interval) -- at the end: -- --
-- >>> let a2 = onFor 3 . sumFrom 0 -- --> onFor 3 . sumFrom 100 -- --> pure 0 -- -- >>> streamAuto' a2 [1..10] -- [1,3,6,104,109,115,0,0,0,0] ---- -- a --> b --> c associates as a --> (b --> -- c) -- -- This is pretty invaluable for having Autos "step" through a -- series of different Autos, progressing their state from one -- stage to the next. Autos can control when they want to be -- "moved on" from by turning "off" (outputting Nothing). -- -- Note that recursive bindings work just fine, so: -- --
-- >>> let a3 = onFor 2 . pure "hello" -- --> onFor 2 . pure "world" -- --> a3 -- -- >>> let (res3, _) = stepAutoN' 8 a3 () -- -- >>> res3 -- ["hello", "hello", "world", "world", "hello", "hello", "world", "world"] ---- -- the above represents an infinite loop between outputting "hello" and -- outputting "world". -- -- For serialization, an extra byte cost is incurred per invocation of -- -->. For cyclic switches like a3, every time the -- cycle "completes", it adds another layer of --> byte costs. -- For example, initially, saving a3 incurs a cost for the two -- -->s. After a3 loops once, it incurs a cost for -- another two -->s, so it costs four -->s. After -- a3 loops another time, it is like a cost of six -- -->s. So be aware that for cyclic bindings like a3, -- space for serialization grows at O(n). -- -- By the way, it might be worth contrasting this with <|!> -- and <|?> from Control.Auto.Interval, which have -- the same type signatures. Those alternative-y operators always feed -- the input to both sides, run both sides, and output the -- first Just. With <|!>, you can "switch back and -- forth" to the first Auto as soon as the first Auto is -- "on" (Just) again. -- -- -->, in contrast, runs only the first Auto -- until it is off (Nothing)...then runs only the second -- Auto. This transition is one-way, as well. (-->) :: Monad m => Interval m a b -> Auto m a b -> Auto m a b -- | A variation of -->, where the right hand side can also be an -- Interval / Maybe. The entire result is, then, a -- Maybe. Probably less useful than --> in most -- situations. (-?>) :: Monad m => Interval m a b -> Interval m a b -> Interval m a b -- | 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 -> Auto m a b -> Auto m a b -> Auto m a b -- | Takes an Auto who has both a normal output stream and a blip -- stream output stream, where the blip stream emits new Autos. -- -- You can imagine switchFrom_ as a box containing a single -- Auto like the one just described. It feeds its input into the -- contained Auto, and its output stream is the "normal value" -- output stream of the contained Auto. -- -- However, as soon as the blip stream of the contained Auto emits -- a new Auto, it replaces the contained Auto with -- the new one (just after emitting the "normal value"), and the -- whole thing starts all over again. -- -- switchFrom_ a0 will "start" with a0 already -- in the box. -- -- This is mostly useful to allow Autos to "replace themselves" or -- control their own destiny, or the behavior of their successors. -- -- In the following example, a1 is an Auto that behaves -- like a cumulative sum but also outputs a blip stream that will emit an -- Auto containing pure 100 (the Auto that -- always emits 100) after three steps. -- --
-- a1 :: Auto' Int (Int, Blip (Auto' Int Int)) -- a1 = proc x -> do -- sums <- sumFrom 0 -< x -- switchBlip <- inB 4 -< pure 100 -- id -< (sums, switchBlip) -- -- -- alternatively -- a1' = sumFrom 0 &&& (tagBlips (pure 100) . inB 4) ---- -- So, switchFrom_ a1 will be the output of -- count for three steps, and then switch to pure -- 100 afterwards (when the blip stream emits): -- --
-- >>> streamAuto' (switchFrom_ a1) [1..10] -- [1,3,6,10,100,100,100,100,100,100] ---- -- This is fun to use with recursion, so you can get looping switches: -- --
-- a2 :: Auto' Int (Int, Blip (Auto' Int Int)) -- a2 = proc x -> do -- sums <- sumFrom 0 -< x -- switchBlip <- inB 3 -< switchFrom_ a2 -- id -< (c, switchBlip) -- -- -- alternatively -- a2' = sumFrom 0 &&& (tagBlips (switchFrom_ a2') . inB 3) ---- --
-- >>> streamAuto' (switchFrom_ a2) [101..112] -- [ 101, 203, 306 -- first 'sumFrom', on first three items [101, 102, 103] -- , 104, 209, 315 -- second 'sumFrom', on second three items [104, 105, 106] -- , 107, 215, 324 -- third 'sumFrom', on third three items [107, 108, 109] -- , 110, 221, 333 -- final 'sumFrom', on fourth three items [110, 111, 112] -- ] ---- -- Note that this combinator is inherently unserializable, so you are -- going to lose all serialization capabilities if you use this. So sad, -- I know! :( This fact is reflected in the underscore suffix, as per -- convention. -- -- If you want to use switching and have serialization, you can -- use the perfectly serialization-safe alternative, switchFromF, -- which slightly less powerful in ways that are unlikely to be missed in -- practical usage. That is, almost all non-contrived real life usages of -- switchFrom_ can be recovered using switchFromF. switchFrom_ :: Monad m => Auto m a (b, Blip (Auto m a b)) -> Auto m a b -- | You can think of this as a little box containing a single Auto -- inside. Takes two input streams: an input stream of normal values, and -- a blip stream containing Autos. It feeds the input stream into -- the contained Auto...but every time the input blip stream emits -- with a new Auto, replaces the contained Auto with -- the emitted one. Then starts the cycle all over, immediately giving -- the new Auto the received input. -- -- Useful for being able to externally "swap out" Autos for a -- given situation by just emitting a new Auto in the blip stream. -- -- For example, here we push several Autos one after the other -- into the box: sumFrom 0, productFrom -- 1, and count. eachAt_ 4 emits each -- Auto in the given list every four steps, starting on the -- fourth. -- --
-- newAutos :: Auto' Int (Blip (Auto' Int Int)) -- newAutos = eachAt_ 4 [sumFrom 0, productFrom 1, count] -- -- a :: Auto' Int Int -- a = proc i -> do -- blipAutos <- newAutos -< () -- switchOn_ (pure 0) -< (i, blipAutos) -- -- -- alternatively -- a' = switchOn_ (pure 0) . (id &&& newAutos) ---- --
-- >>> streamAuto' a [1..12] -- [ 1, 3, 6 -- output from sumFrom 0 -- , 4, 20, 120 -- output from productFrom 1 -- , 0, 1, 2, 3, 4, 5] -- output from count ---- -- Like switchFrom_, this combinator is inherently unserializable. -- So if you use it, you give up serialization for your Autos. -- This is reflected in the underscore suffix. -- -- If you wish to have the same switching devices but keep serialization, -- you can use switchOnF, which is slightly less powerful, but -- should be sufficient for all practical use cases. switchOn_ :: Monad m => Auto m a b -> Auto m (a, Blip (Auto m a b)) b -- | Essentially identical to switchOn_, except instead of taking in -- a blip stream of new Autos to put into the box, takes a blip -- stream of c --- and switchOnF uses the c to -- create the new Auto to put in the box. -- -- Here is the equivalent of the two examples from switchOn_, -- implemented with switchOnF; see the documentatino for -- switchOn_ for a description of what they are to do. -- --
-- newAuto :: Int -> Auto' Int Int -- newAuto 1 = sumFrom 0 -- newAuto 2 = productFrom 1 -- newAuto 3 = count -- newAuto _ = error "Do you expect rigorous error handling from a toy example?" -- -- a :: Auto' Int Int -- a = proc i -> do -- blipAutos <- eachAt 4 [1,2,3] -< () -- switchOnF_ newAuto (pure 0) -< (i, blipAutos) ---- --
-- >>> streamAuto' a [1..12] -- [ 1, 3, 6 -- output from sumFrom 0 -- , 4, 20, 120 -- output from productFrom 1 -- , 0, 1, 2, 3, 4, 5] -- output from count ---- -- Instead of sending in the "replacement Auto", sends in a -- number, which corresponds to a specific replacement Auto. -- -- As you can see, all of the simple examples from switchOn_ can -- be implemented in switchOnF...and so can most real-life -- examples. The advantage is that switchOnF is serializable, and -- switchOn_ is not. switchOnF :: (Monad m, Serialize c) => (c -> Auto m a b) -> Auto m a b -> Auto m (a, Blip c) b -- | The non-serializing/non-resuming version of switchOnF. You sort -- of might as well use switchOn_; this version might give rise to -- more "disciplined" code, however, by being more restricted in power. switchOnF_ :: Monad m => (c -> Auto m a b) -> Auto m a b -> Auto m (a, Blip c) b -- | Essentially identical to switchFrom_, except insead of the -- Auto outputting a blip stream of new Autos to replace -- itself with, it emits a blip stream of c --- and -- switchFromF uses the c to create the new Auto. -- -- Here is the equivalent of the two examples from switchFrom_, -- implemented with switchFromF; see the documentation for -- switchFrom_ for a description of what they are to do. -- --
-- a1 :: Auto' Int (Int, Blip Int) -- a1 = proc x -> do -- sums <- sumFrom 0 -< x -- switchBlip <- inB 4 -< 100 -- id -< (sums, switchBlip) -- -- -- alternatively -- a1' = sumFrom 0 &&& (tagBlips 100 . inB 4) ---- --
-- >>> streamAuto' (switchFromF (\x -> (x,) <$> never) a1) [1..10] -- [1,3,6,10,100,100,100,100,100,100] ---- --
-- a2 :: Auto' Int (Int, Blip ()) -- a2 = proc x -> do -- sums <- sumFrom 0 -< x -- switchBlip <- inB 3 -< () -- id -< (sums, switchBlip) -- -- -- alternatively -- a2' = sumFrom 0 &&& (tagBlips () . inB 3) ---- --
-- >>> streamAuto' (switchFromF (const a2) a2) [101..112] -- [ 101, 203, 306 -- first 'sumFrom', on first three items [101, 102, 103] -- , 104, 209, 315 -- second 'sumFrom', on second three items [104, 105, 106] -- , 107, 215, 324 -- third 'sumFrom', on third three items [107, 108, 109] -- , 110, 221, 333] -- final 'sumFrom', on fourth three items [110, 111, 112] ---- -- Or, if you're only ever going to use a2 in switching form: -- --
-- a2s :: Auto' Int Int -- a2s = switchFromF (const a2s) $ proc x -> do -- sums <- sumFrom 0 -< x -- switchBlip <- inB 3 -< () -- id -< (c, swichBlip) -- -- -- or -- a2s' = switchFromF (const a2s') -- $ sumFrom 0 &&& (tagBlips () . inB 3) ---- --
-- >>> streamAuto' a2s [101..112] -- [101, 203, 306, 104, 209, 315, 107, 215, 324, 110, 221, 333] ---- -- As you can see, all of the simple examples from switchFrom_ can -- be implemented in switchFromF...and so can most real-life -- examples. The advantage is that switchFromF is serializable, -- and switchFrom_ is not. -- -- Note that for the examples above, instead of using const, we -- could have actually used the input parameter to create a new -- Auto based on what we outputted. switchFromF :: (Monad m, Serialize c) => (c -> Auto m a (b, Blip c)) -> Auto m a (b, Blip c) -> Auto m a b -- | The non-serializing/non-resuming version of switchFromF. You -- sort of might as well use switchFrom_; this version might give -- rise to more "disciplined" code, however, by being more restricted in -- power. switchFromF_ :: Monad m => (c -> Auto m a (b, Blip c)) -> Auto m a (b, Blip c) -> Auto m a b -- | Takes an innocent Auto and wraps a "reset button" around it. It -- behaves just like the original Auto at first, but when the -- input blip stream emits, the internal Auto is reset back to the -- beginning. -- -- Here we have sumFrom wrapped around a reset button, and we -- send in a blip stream that emits every 4 steps; so every 4th step, the -- whole summer resets. -- --
-- >>> let a = resetOn (sumFrom 0) . (id &&& every 4) -- -- >>> streamAuto' a [101..112] -- [ 101, 203, 306 -- , 104, 209, 315 -- resetted! -- , 107, 215, 324 -- resetted! -- , 110, 221, 333] -- resetted! --resetOn :: Monad m => Auto m a b -> Auto m (a, Blip c) b -- | Gives an Auto the ability to "reset" itself on command -- -- Basically acts like fmap fst -- --
-- fmap fst :: Monad m => Auto m a (b, Blip c) -> Auto m a b ---- -- But...whenever the blip stream emits..."resets" the Auto back -- to the original state, as if nothing ever happened. -- -- Note that this resetting happens on the step after the blip -- stream emits. -- -- Here is a summer that sends out a signal to reset itself whenever the -- cumulative sum reaches 10 or higher: -- --
-- limitSummer :: Auto' Int (Int, Blip ()) -- limitSummer = (id &&& became (>= 10)) . sumFrom 0 ---- -- And now we throw it into resetFrom: -- --
-- resettingSummer :: Auto' Int Int -- resettingSummer = resetFrom limitSummer ---- --
-- >>> streamAuto' resettingSummer [1..10] -- [ 1, 3, 6, 10 -- and...reset! -- , 5, 11 -- and...reset! -- , 7, 15 -- and...reset! -- , 9, 19 ] --resetFrom :: Monad m => Auto m a (b, Blip c) -> Auto m a b -- | This module provides Autos (purely) generating entropy in the -- form of random or noisy processes, as well as Autos to -- purify/seal Autos with underlying entropy. -- -- Note that every Auto and combinator here is completely -- deterministic --- given the same initial seed, one would expect the -- same stream of outputs on every run. Furthermore, if a serializable -- Auto is serialized and resumed, it will continue along the -- deterministic path dictated by the original seed given. -- -- All of these Autos and combinators come in three flavors: one -- serializing one that works with any serializable RandomGen -- instance, one serializing one that works specifically with -- StdGen from System.Random, and one that takes any -- RandomGen (including StdGen) and runs it without the -- ability to serialize and resume deterministically. -- -- The reason why there's a specialized StdGen version for all of -- these is that StdGen actually doesn't have a Serialize -- instance, so a rudimentary serialization process is provded with the -- StdGen versions. -- -- The first class of generators take arbitrary g -> (b, g) -- functions: "Generate a random b, using the given function, -- and replace the seed with the resulting seed". Most "random" functions -- follow this pattern, including random and randomR, and -- if you are using something from MonadRandom, then you can use -- the runRand function to turn a Rand g b into a -- g -> (b, g), as well: -- --
-- runRand :: RandomGen g => Rand g b -> (g -> (b, g)) ---- -- These are useful for generating noise...a new random value at every -- step. They are entropy sources. -- -- Alternatively, if you want to give up parallelizability and -- determinism and have your entire Auto be sequential, you can -- make your entire Auto run under Rand or RandT as -- its internal monad, from MonadRandom. -- --
-- Auto (Rand g) a b -- Auto (RandT g m) a b ---- -- In this case, if you wanted to pull a random number, you could do: -- --
-- effect random :: (Random r, RandomGen g) => Auto (Rand g) a r -- effect random :: (Random r, RandomGen g) => Auto (RandT g m) a r ---- -- Which pulls a random r from "thin air" (from the internal -- Rand monad). -- -- However, you lose a great deal of determinism from this method, as -- your Autos are no longer deterministic with a given seed...and -- resumability becomes dependent on starting everything with the same -- seed every time you re-load your Auto. Also, Auto's are -- parallelizable, while Auto (Rand g)s are not. -- -- As a compromise, you can then "seal" away the underlying monad with -- sealRandom, which takes an Auto (RandT g m) a -- b, a starting g, and turns it into a normal -- Auto m a b, with no underlying randomness monad. -- -- In this way, you can run any Auto under Rand or -- RandT as if it was a normal Auto "without" underlying -- randomness. This lets you compose your sequential/non-parallel parts -- in Rand...and the later, use it as a part of a -- parallelizable/potentially non-sequential Auto'. It's also -- convenient because you don't have to manually split and pass around -- seeds to every Auto that requires entropy. -- -- The other generators given are for useful random processes you might -- run into. The first is a Blip stream that emits at random times -- with the given frequencyprobability. The second works Interval/ -- semantics from Control.Auto.Interval, and is a stream that is -- "on" or "off", chunks at a time, for random lengths. The average -- length of each on or off period is controlled by the parameter you -- pass in. module Control.Auto.Process.Random -- | Given a seed-consuming generating function of form g -> (b, -- g) (where g is the seed, and b is the result) -- and an initial seed, return an Auto that continually generates -- random values using the given generating funcion. -- -- You'll notice that most of the useful functions from -- System.Random fit this form: -- --
-- random :: RandomGen g => g -> (b, g) -- randomR :: RandomGen g => (b, b) -> (g -> (b, g)) ---- -- If you are using something from MonadRandom, then you can use -- the runRand function to turn a Rand g b into a -- g -> (b, g): -- --
-- runRand :: RandomGen g => Rand g b -> (g -> (b, g)) ---- -- Here is an example using stdRands (for StdGen), but -- rands works exactly the same way, I promise! -- --
-- >>> let g = mkStdGen 8675309 -- -- >>> let a = stdRands (randomR (1,100)) g :: Auto' a Int -- -- >>> let (res, _) = stepAutoN' 10 a () -- -- >>> res -- [67, 15, 97, 13, 55, 12, 34, 86, 57, 42] ---- -- Yeah, if you are using StdGen from System.Random, you'll -- notice that StdGen has no Serialize instance, so you -- can't use it with this; you have to either use stdRands or -- rands_ (if you don't want serialization/resumability). -- -- In the context of these generators, resumability basically means -- deterministic behavior over re-loads...if "reloading", it'll ignore -- the seed you pass in, and use the original seed given when originally -- saved. rands :: (Serialize g, RandomGen g) => (g -> (b, g)) -> g -> Auto m a b -- | Like rands, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of rands for more information on this -- Auto. stdRands :: (StdGen -> (b, StdGen)) -> StdGen -> Auto m a b -- | The non-serializing/non-resuming version of rands. rands_ :: RandomGen g => (g -> (b, g)) -> g -> Auto m a b -- | Like rands, except taking a "monadic" random seed function -- g -> m (b, g), instead of g -> (b, g). Your -- random generating function has access to the underlying monad. -- -- If you are using something from MonadRandom, then you can use -- the runRandT function to turn a RandT g m b -- into a g -> m (b, g): -- --
-- runRandT :: (Monad m, RandomGen g) -- => RandT g m b -> (g -> m (b, g)) --randsM :: (Serialize g, RandomGen g, Monad m) => (g -> m (b, g)) -> g -> Auto m a b -- | Like randsM, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of randsM for more information on this -- Auto. stdRandsM :: Monad m => (StdGen -> m (b, StdGen)) -> StdGen -> Auto m a b -- | The non-serializing/non-resuming version of randsM. randsM_ :: (RandomGen g, Monad m) => (g -> m (b, g)) -> g -> Auto m a b -- | Takes a "random function", or "random arrow" --- a function taking an -- input value and a starting seed/entropy generator and returning a -- result and an ending seed/entropy generator --- and turns it into an -- Auto that feeds its input into such a function and outputs the -- result, with a new seed every time. -- --
-- >>> let f x = randomR (0 :: Int, x) -- -- >>> streamAuto' (arrRandStd f (mkStdGen 782065)) [1..10] -- -- [1,2,3,4,5,6,7,8,9,10] <- upper bounds -- [1,2,0,1,5,3,7,6,8,10] -- random number from 0 to upper bound ---- -- If you are using something from MonadRandom, then you can use -- the (runRand .) function to turn a a -> -- Rand g b into a a -> g -> (b, g): -- --
-- (runRand .) :: RandomGen g => (a -> Rand g b) -> (a -> g -> (b, g)) ---- -- (This is basically mkState, specialized.) arrRand :: (Serialize g, RandomGen g) => (a -> g -> (b, g)) -> g -> Auto m a b -- | Like arrRand, except the result is the result of a monadic -- action. Your random arrow function has access to the underlying monad. -- -- If you are using something from MonadRandom, then you can use -- the (runRandT .) function to turn a a -> -- RandT m g b into a a -> g -> m (b, g): -- --
-- (runRandT .) :: RandomGen g => (a -> RandT g b) -> (a -> g -> m (b, g)) --arrRandM :: (Monad m, Serialize g, RandomGen g) => (a -> g -> m (b, g)) -> g -> Auto m a b -- | Like arrRand, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of arrRand for more information on this -- Auto. arrRandStd :: (a -> StdGen -> (b, StdGen)) -> StdGen -> Auto m a b -- | Like arrRandM, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of arrRandM for more information on this -- Auto. arrRandStdM :: (a -> StdGen -> m (b, StdGen)) -> StdGen -> Auto m a b -- | The non-serializing/non-resuming version of arrRand. arrRand_ :: RandomGen g => (a -> g -> (b, g)) -> g -> Auto m a b -- | The non-serializing/non-resuming version of arrRandM. arrRandM_ :: RandomGen g => (a -> g -> m (b, g)) -> g -> Auto m a b -- | Simulates a Bernoulli Process: a process of sequential -- independent trials each with a success of probability p. -- -- Implemented here is an Auto producing a blip stream that emits -- whenever the bernoulli process succeeds with the value of the received -- input of the Auto, with its probability of succuss per each -- trial as the Double parameter. -- -- It is expected that, for probability p, the stream will emit -- a value on average once every 1/p ticks. bernoulli :: (Serialize g, RandomGen g) => Double -> g -> Auto m a (Blip a) -- | Like bernoulli, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of bernoulli for more information on this -- Auto. stdBernoulli :: Double -> StdGen -> Auto m a (Blip a) -- | The non-serializing/non-resuming version of bernoulli. bernoulli_ :: RandomGen g => Double -> g -> Auto m a (Blip a) -- | An Interval that is "on" and "off" for contiguous but random -- intervals of time...when "on", allows values to pass as "on" -- (Just), but when "off", suppresses all incoming values -- (outputing Nothing). -- -- You provide a Double, an l parameter, representing the -- averageexpected length of each onoff interval. -- -- The distribution of interval lengths follows a Geometric -- Distribution. This distribution is, as we call it in maths, -- "memoryless", which means that the "time left" that the Auto -- will be "on" or "off" at any given time is going to be, on average, -- the given l parameter. -- -- Internally, the "toggling" events follow a bernoulli process with a -- p parameter of 1 / l. randIntervals :: (Serialize g, RandomGen g) => Double -> g -> Interval m a a -- | Like randIntervals, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of randIntervals for more information on -- this Auto. stdRandIntervals :: Double -> StdGen -> Interval m a a -- | The non-serializing/non-resuming version of randIntervals. randIntervals_ :: RandomGen g => Double -> g -> Interval m a a -- | Takes an Auto over an Rand or RandT underlying -- monad as an entropy source, and "seals it away" to just be a normal -- Auto or Auto': -- --
-- sealRandom :: Auto (Rand g) a b -> g -> Auto' a b ---- -- You can now compose your entropic Auto with other Autos -- (using ., and other combinators) as if it were a normal -- Auto. -- -- Useful because you can create entire programs that have access to an -- underlying entropy souce by composing with Rand...and then, at -- the end of it all, use/compose it with normal Autos as if it -- were a "pure" Auto. sealRandom :: (RandomGen g, Serialize g, Monad m) => Auto (RandT g m) a b -> g -> Auto m a b -- | Like sealRandom, but specialized for StdGen from -- System.Random, so that you can serialize and resume. This is -- needed because StdGen doesn't have a Serialize instance. -- -- See the documentation of sealRandom for more information on -- this combinator. sealRandomStd :: Monad m => Auto (RandT StdGen m) a b -> StdGen -> Auto m a b -- | The non-serializing/non-resuming version of sealRandom_. The -- random seed is not re-loaded/resumed, so every time you resume, the -- stream of available randomness begins afresh. sealRandom_ :: (RandomGen g, Serialize g, Monad m) => Auto (RandT g m) a b -> g -> Auto m a b -- | bernoulli, but uses an underlying entropy source -- (MonadRandom) to get its randomness from, instead of an -- initially passed seed. -- -- You can recover exactly bernoulli p by using -- sealRandom (bernoulliMR p). -- -- See sealRandom for more information. bernoulliMR :: MonadRandom m => Double -> Auto m a (Blip a) -- | randIntervals, but uses an underlying entropy source -- (MonadRandom) to get its randomness from, instead of an -- initially passed seed. -- -- You can recover exactly randIntervals l by using -- sealRandom (randIntervalsMR l). -- -- See sealRandom for more information. randIntervalsMR :: MonadRandom m => Double -> Interval m a a -- | This module serves as the main entry point for the library; these are -- all basically re-exports. The re-exports are chosen so you can start -- doing "normal things" off the bat, including all of the types used in -- this library. -- -- Conspicuously missing are the most of the tools for working with -- Interval, Blip streams, switches, and the "collection" -- autos; those are all pretty heavy, and if you do end up working with -- any of those tools, simply importing the appropriate module should -- give you all you need. -- -- See the tutorial if you need help getting started! module Control.Auto -- | The Auto type. For this library, an Auto semantically -- representsdenotes a a relationship/ between an input and an -- output that is preserved over multiple steps, where that relationship -- is (optionally) maintained within the context of a monad. -- -- A lot of fancy words, I know...but you can think of an Auto as -- nothing more than a "stream transformer" of value streams. A stream of -- sequential input values come in one at a time, and a stream of outputs -- pop out one at a time, as well. -- -- Using the streamAuto function, you can "unwrap" the inner -- value stream transformer from any Auto: if a :: Auto -- m a b, streamAuto lets you turn it into an [a] -> -- m [b]. "Give me a stream of as, one at a time, and I'll -- give you a list of bs, matching a relationship to your stream -- of as." -- --
-- -- unwrap your inner [a] -> m [b]! -- streamAuto :: Monad m => Auto m a b -> ([a] -> m [b]) ---- -- You can also turn an Auto m a b into an effects -- stream that executes effects sequentially with -- toEffectStream and streamAutoEffects, so you can run -- it with a ListT-compatible library like pipes. -- -- There's a handy type synonym Auto' for relationships that don't -- really need a monadic context; the m is just Identity: -- --
-- type Auto' = Auto Identity ---- -- So if you had an a :: Auto' a b, you can use -- streamAuto' to "unwrap" the inner stream transformer, [a] -- -> [b]. -- --
-- -- unwrap your inner [a] -> [b]! -- streamAuto' :: Auto' a b -> ([a] -> [b]) ---- -- All of the Autos given in this library maintain some sort of -- semantic relationship between streams --- for some, the outputs might -- be the inputs with a function applied; for others, the outputs might -- be the cumulative sum of the inputs. -- -- See the tutorial for more information! -- -- Operationally, an Auto m a b is implemented as a -- "stateful function". A function from an a where, every time -- you "apply" it, you get a b and an "updated -- Auto"/function with updated state. -- -- You can get this function using stepAuto: -- --
-- stepAuto :: Auto m a b -> (a -> m (b, Auto m a b)) ---- -- Or, for Auto', stepAuto': -- --
-- stepAuto' :: Auto' a b -> (a -> (b, Auto' a b)) ---- -- "Give me an a and I'll give you a b and your -- "updated" Auto". -- -- Autos really are mostly useful because they can be composed, -- chained, and modified using their various typeclass instances, like -- Category, Applicative, Functor, Arrow, -- etc., and also with the combinators in this library. You can build -- complex programs as a complex Auto by building up smaller and -- smaller components. See the tutorial for more information on this. -- -- This type also contains information on its own serialization, so you -- can serialize and re-load the internal state to binary or disk. See -- the "serialization" section in the documentation for -- Control.Auto.Core, or the documentation for mkAutoM for -- more details. data Auto m a b -- | Special case of Auto where the underlying Monad is -- Identity. -- -- Instead of "wrapping" an [a] -> m [b], it "wraps" an -- [a] -> [b]. type Auto' = Auto Identity -- | When used in the context of an input or output of an Auto, a -- Blip a represents a stream that occasionally, at -- "independent" or "discrete" points, emits a value of type a. -- -- Contrast this to Interval, where things are meant to be "on" -- or "off" for contiguous chunks at a time; blip streams are "blippy", -- and Intervals are "chunky". -- -- It's here mainly because it's a pretty useful abstraction in the -- context of the many combinators found in various modules of this -- library. If you think of an Auto m a (Blip b) -- as producing a "blip stream", then there are various combinators and -- functions that are specifically designed to manipulate blip streams. -- -- For the purposes of the semantics of what Blip is supposed to -- represent, its constructors are hidden. (Almost) all of the various -- Blip combinators (and its very useful Functor instance) -- "preserve Blipness" --- one-at-a-time occurrences remain -- one-at-a-time under all of these combinators, and you should have -- enough so that direct access to the constructor is not needed. -- -- If you are creating a framework, library, or backend, you might want -- to manually create blip stream-producing Autos for your users -- to access. In this case, you can import the constructors and useful -- internal (and, of course, semantically unsafe) functions from -- Control.Auto.Blip.Internal. data Blip a -- | Represents a relationship between an input and an output, where the -- output can be "on" or "off" (using Just and Nothing) for -- contiguous chunks of time. -- -- Just a type alias for Auto m a (Maybe -- b). If you ended up here with a link...no worries! If you see -- Interval m a b, just think Auto m a -- (Maybe b) for type inference/type checking purposes. -- -- If you see something of type Interval, you can rest assured -- that it has "interval semantics" --- it is on and off for meaningfully -- contiguous chunks of time, instead of just on and off willy nilly. If -- you have a function that expects an Interval, then the function -- expects its argument to behave in this way. type Interval m a b = Auto m a (Maybe b) -- | Interval, specialized with Identity as its underlying -- Monad. Analogous to Auto' for Auto. type Interval' a b = Auto' a (Maybe b) -- | Runs the Auto through one step. -- -- That is, given an Auto m a b, returns a function that -- takes an a and returns a b and an "updated"/"next" -- Auto; an a -> m (b, Auto m a b). -- -- This is the main way of running an Auto "step by step", so if -- you have some sort of game loop that updates everything every "tick", -- this is what you're looking for. At every loop, gather input -- a, feed it into the Auto, "render" the result -- b, and get your new Auto to run the next time. -- -- Here is an example with sumFrom 0, the Auto -- whose output is the cumulative sum of the inputs, and an underying -- monad of Identity. Here, -- --
-- stepAuto :: Auto Identity Int Int -- -> (Int -> Identity (Int, Auto Identity Int Int)) ---- -- Every time you "step", you give it an Int and get a resulting -- Int (the cumulative sum) and the "updated Auto", with -- the updated accumulator. -- --
-- >>> let a0 :: Auto Identity Int Int -- a0 = sumFrom 0 -- -- >>> let Identity (res1, a1) = stepAuto a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let Identity (res2, a2) = stepAuto a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let Identity (res3, _ ) = stepAuto a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- By the way, for the case where your Auto is under -- Identity, we have a type synomym Auto'...and a -- convenience function to make "running" it more streamlined: -- --
-- >>> let a0 :: Auto' Int Int -- a0 = sumFrom 0 -- -- >>> let (res1, a1) = stepAuto' a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let (res2, a2) = stepAuto' a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let (res3, _ ) = stepAuto' a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- But, if your Auto actaully has effects when being stepped, -- stepAuto will execute them: -- --
-- >>> let a0 :: Auto IO Int Int -- a0 = effect (putStrLn "hey!") *> sumFrom 0 -- -- >>> (res1, a1) <- stepAuto a0 4 -- run with 4 -- hey! -- IO effect -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> (res2, a2) <- stepAuto a1 5 -- run with 5 -- hey! -- IO effect -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> (res3, _ ) <- stepAuto a2 3 -- run with 3 -- hey! -- IO effect -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- (Here, effect (putStrLn "hey") is an -- Auto IO Int (), which ignores its input and just -- executes putStrLn "hey" every time it is run. When we -- use *> from Control.Applicative, we "combine" the two -- Autos together and run them both on each input (4, 5, -- 3...)...but for the "final" output at the end, we only return the -- output of the second one, sumFrom 0 (5, 9, 12...)) -- -- If you think of an Auto m a b as a "stateful function" -- a -> m b, then stepAuto lets you "run" it. -- -- In order to directly run an Auto on a stream, an [a], -- use streamAuto. That gives you an [a] -> m [b]. stepAuto :: Monad m => Auto m a b -> a -> m (b, Auto m a b) -- | Runs an Auto' through one step. -- -- That is, given an Auto' a b, returns a function that -- takes an a and returns a b and an "updated"/"next" -- Auto'; an a -> (b, Auto' a b). -- -- See stepAuto documentation for motivations, use cases, and more -- details. You can use this instead of stepAuto when your -- underyling monad is Identity, and your Auto doesn't -- produce any effects. -- -- Here is an example with sumFrom 0, the Auto' -- whose output is the cumulative sum of the inputs -- --
-- stepAuto' :: Auto' Int Int -- -> (Int -> (Int, Auto' Int Int)) ---- -- Every time you "step", you give it an Int and get a resulting -- Int (the cumulative sum) and the "updated Auto'", with -- the updated accumulator. -- --
-- >>> let a0 :: Auto' Int Int -- a0 = sumFrom 0 -- -- >>> let (res1, a1) = stepAuto' a0 4 -- run with 4 -- -- >>> res1 -- 4 -- the cumulative sum, 4 -- -- >>> let (res2, a2) = stepAuto' a1 5 -- run with 5 -- -- >>> res2 -- 9 -- the cumulative sum, 4 + 5 -- -- >>> let (res3, _ ) = stepAuto' a2 3 -- run with 3 -- -- >>> res3 -- 12 -- the cumulative sum, 4 + 5 + 3 ---- -- If you think of an Auto' a b as a "stateful function" -- a -> b, then stepAuto' lets you "run" it. -- -- In order to directly run an Auto' on a stream, an [a], -- use streamAuto'. That gives you an [a] -> [b]. stepAuto' :: Auto' a b -> a -> (b, Auto' a b) -- | Like stepAuto, but drops the "next Auto" and just gives -- the result. evalAuto :: Monad m => Auto m a b -> a -> m b -- | Like stepAuto', but drops the "next Auto'" and just -- gives the result. evalAuto for Auto'. evalAuto' :: Auto' a b -> a -> b -- | 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 :: Monad m => Auto m a b -> [a] -> m [b] -- | 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] --streamAuto' :: Auto' a b -> [a] -> [b] -- | 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 :: Monad m => Int -> Auto m a b -> a -> m ([b], Auto m a b) -- | 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] --stepAutoN' :: Int -> Auto' a b -> a -> ([b], Auto' a b) -- | Encode an Auto and its internal state into a ByteString. encodeAuto :: Auto m a b -> ByteString -- | Resume an Auto from its ByteString serialization, -- giving a Left if the deserialization is not possible. decodeAuto :: Auto m a b -> ByteString -> Either String (Auto m a b) -- | Give a FilePath and an Auto, and readAuto will -- attempt to resume the saved state of the Auto from disk, -- reading from the given FilePath. Will return Left upon a -- decoding error, with the error, and Right if the decoding is -- succesful. readAuto :: FilePath -> Auto m a b -> IO (Either String (Auto m a b)) -- | Given a FilePath and an Auto, serialize and freeze the -- state of the Auto as binary to that FilePath. writeAuto :: FilePath -> Auto m a b -> IO () -- | Takes an Auto that is serializable/resumable and returns an -- Auto that is not. That is, when it is "saved", saves no data, -- and when it is "resumed", resets itself back to the initial -- configuration every time; in other words, decodeAuto -- (unserialize a) bs = Right (unserialize a). Trying to "resume" it -- will just always give itself, unchanged. unserialize :: Monad m => Auto m a b -> Auto m a b -- | A special Auto that acts like the id Auto, but -- forces results as they come through to be fully evaluated, when -- composed with other Autos. -- -- TODO: Test if this really works forcer :: NFData a => Auto m a a -- | A special Auto that acts like the id Auto, but -- forces results as they come through to be evaluated to Weak Head -- Normal Form, with seq, when composed with other Autos. -- -- TODO: Test if this really works seqer :: Auto m a a -- | Swaps out the underlying Monad of an Auto using the -- given monad morphism "transforming function", a natural -- transformation. -- -- Basically, given a function to "swap out" any m a with an -- m' a, it swaps out the underlying monad of the Auto. -- -- This forms a functor, so you rest assured in things like this: -- --
-- hoistA id == id -- hoistA f a1 . hoistA f a2 == hoistA f (a1 . a2) --hoistA :: (Monad m, Monad m') => (forall c. m c -> m' c) -> Auto m a b -> Auto m' a b -- | Generalizes an Auto' a b to an Auto m a -- b' for any Monad m, using hoist. -- -- You generally should be able to avoid using this if you never directly -- write any Auto's and always write 'Auto m' parameterized over -- all Monads, but...in case you import one from a library or -- something, you can use this. generalizeA :: Monad m => Auto' a b -> Auto m a b -- | Applies the given "monadic function" (function returning a monadic -- action) to every incoming item; the result is the result of executing -- the action returned. -- -- Note that this essentially lifts a "Kleisli arrow"; it's like -- arr, but for "monadic functions" instead of normal functions: -- --
-- arr :: (a -> b) -> Auto m a b -- arrM :: (a -> m b) -> Auto m a b ---- --
-- arrM f . arrM g == arrM (f <=< g) ---- -- One neat trick you can do is that you can "tag on effects" to a normal -- Auto by using *> from Control.Applicative. For -- example: -- --
-- >>> let a = arrM print *> sumFrom 0 -- -- >>> ys <- streamAuto a [1..5] -- 1 -- IO output -- 2 -- 3 -- 4 -- 5 -- -- >>> ys -- [1,3,6,10,15] -- the result ---- -- Here, a behaves "just like" sumFrom -- 0...except, when you step it, it prints out to stdout as a -- side-effect. We just gave automatic stdout logging behavior! arrM :: (a -> m b) -> Auto m a b -- | Like arr, but applies the function to the previous value -- of the input, instead of the current value. Used for the same purposes -- as lastVal: to manage recursive bindings. -- -- Warning: Don't use this to do imperative programming! -- --
-- arrD id == lastVal ---- --
-- >>> streamAuto' (arrD negate 100) [1..10] -- [100,-1,-2,-3,-4,-5,-6,-7,-8,-9] --arrD :: Serialize b => (a -> b) -> b -> Auto m a b -- | Construct an Auto from a "folding" function: b -> a -- -> b yields an Auto m a b. Basically acts like -- a foldl or a scanl. There is an internal accumulator -- that is "updated" with an a at every step. Must be given an -- initial accumulator. -- -- Example: an Auto that sums up all of its input. -- --
-- >>> let summer = accum (+) 0 -- -- >>> let (sum1, summer') = stepAuto' summer 3 -- -- >>> sum1 -- 3 -- -- >>> let (sum2, summer'') = stepAuto' summer' 10 -- -- >>> sum2 -- 13 -- -- >>> streamAuto' summer'' [1..10] -- [14,16,19,23,28,34,41,49,58,68] ---- -- If your accumulator b does not have a Serialize -- instance, then you should either write a meaningful one, or throw away -- serializability and use accum_. accum :: Serialize b => (b -> a -> b) -> b -> Auto m a b -- | A version of accum, where the internal accumulator isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See accum for more details. -- -- Useful if your accumulator b cannot have a meaningful -- Serialize instance. accum_ :: (b -> a -> b) -> b -> Auto m a b -- | Construct an Auto from a "monadic" "folding" function: b -- -> a -> m b yields an Auto m a b. Basically -- acts like a foldM or scanM (if it existed). here is an -- internal accumulator that is "updated" with an input a with -- the result of the executed m b at every step. Must be given -- an initial accumulator. -- -- See accum for more details. -- -- If your accumulator b does not have a Serialize -- instance, then you should either write a meaningful one, or throw away -- serializability and use accumM_. accumM :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b -- | A version of 'accumM_, where the internal accumulator isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See accumM for more details. -- -- Useful if your accumulator b cannot have a meaningful -- Serialize instance. accumM_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b -- | A "delayed" version of accum, where the first output is the -- initial state of the accumulator, before applying the folding -- function. Useful in recursive bindings. -- --
-- >>> let summerD = accumD (+) 0 -- -- >>> let (sum1, summerD') = stepAuto' summerD 3 -- -- >>> sum1 -- 0 -- -- >>> let (sum2, summerD'') = stepAuto' summerD' 10 -- -- >>> sum2 -- 3 -- -- >>> streamAuto' summerD'' [1..10] -- [13,14,16,19,23,28,34,41,49,58] ---- -- (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) -> b -> Auto m a b -- | The non-resuming/non-serializing version of accumD. accumD_ :: (b -> a -> b) -> b -> Auto m a b -- | A "delayed" version of accumM, where the first output is the -- initial state of the accumulator, before applying the folding -- function. Useful in recursive bindings. accumMD :: (Serialize b, Monad m) => (b -> a -> m b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of accumMD. accumMD_ :: Monad m => (b -> a -> m b) -> b -> Auto m a b -- | Construct an Auto from a state transformer: an a -> s -- -> (b, s) gives you an Auto m a b, for any -- Monad m. At every step, it takes in the a -- input, runs the function with the stored internal state, returns the -- b result, and now contains the new resulting state. You have -- to intialize it with an initial state, of course. -- -- From the "stream transformer" point of view, this is rougly equivalent -- to mapAccumL from Data.List, with the function's -- arguments and results in the backwards order. -- --
-- streamAuto' (mkState f s0) = snd . mapAccumL (\s x -> swap (f x s)) ---- -- Try not to use this if it's ever avoidable, unless you're a framework -- developer or something. Try make something by combining/composing the -- various Auto combinators. -- -- If your state s does not have a Serialize instance, -- then you should either write a meaningful one, provide the -- serialization methods manually with mkState', or throw away -- serializability and use mkState_. mkState :: Serialize s => (a -> s -> (b, s)) -> s -> Auto m a b -- | Construct an Auto from a "monadic" state transformer: a -- -> s -> m (b, s) gives you an Auto m a b. -- At every step, it takes in the a input, runs the function -- with the stored internal state and "executes" the m (b, s) to -- get the b output, and stores the s as the new, -- updated state. Must be initialized with an initial state. -- -- Try not to use this if it's ever avoidable, unless you're a framework -- developer or something. Try make something by combining/composing the -- various Auto combinators. -- -- This version is a wrapper around mkAuto, that keeps track of -- the serialization and re-loading of the internal state for you, so you -- don't have to deal with it explicitly. -- -- If your state s does not have a Serialize instance, -- then you should either write a meaningful one, provide the -- serialization methods manually with mkStateM', or throw away -- serializability and use mkStateM_. mkStateM :: Serialize s => (a -> s -> m (b, s)) -> s -> Auto m a b -- | A version of mkState, where the internal state isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See mkState for more details. -- -- Useful if your state s cannot have a meaningful -- Serialize instance. mkState_ :: (a -> s -> (b, s)) -> s -> Auto m a b -- | A version of mkStateM, where the internal state isn't -- serialized. It can be "saved" and "loaded", but the state is lost in -- the process. -- -- See mkStateM for more details. -- -- Useful if your state s cannot have a meaningful -- Serialize instance. mkStateM_ :: (a -> s -> m (b, s)) -> s -> Auto m a b -- | To get every output, executes the monadic action and returns the -- result as the output. Always ignores input. -- -- This is basically like an "effectful" pure: -- --
-- pure :: b -> Auto m a b -- effect :: m b -> Auto m a b ---- -- The output of pure is always the same, and the output of -- effect is always the result of the same monadic action. Both -- ignore their inputs. -- -- Fun times when the underling Monad is, for instance, -- Reader. -- --
-- >>> let a = effect ask :: Auto (Reader b) a b -- -- >>> let r = evalAuto a () :: Reader b b -- -- >>> runReader r "hello" -- "hello" -- -- >>> runReader r 100 -- 100 ---- -- If your underling monad has effects (IO, State, -- Maybe, Writer, etc.), then it might be fun to take -- advantage of *> from Control.Applicative to "tack on" -- an effect to a normal Auto: -- --
-- >>> let a = effect (modify (+1)) *> sumFrom 0 :: Auto (State Int) Int Int -- -- >>> let st = streamAuto a [1..10] -- -- >>> let (ys, s') = runState st 0 -- -- >>> ys -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> s' -- 10 ---- -- Out Auto a behaves exactly like sumFrom -- 0, except at each step, it also increments the underlying/global -- state by one. It is sumFrom 0 with an "attached -- effect". effect :: m b -> Auto m a b -- | Analogous to iterate from Prelude. Keeps accumulator -- value and continually applies the function to the accumulator at every -- step, outputting the result. -- -- The first result is the initial accumulator value. -- --
-- >>> take 10 . streamAuto' (iterator (*2) 1) $ repeat () -- [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] --iterator :: Serialize b => (b -> b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of iterator. iterator_ :: (b -> b) -> b -> Auto m a b -- | Like iterator, but with a monadic function. iteratorM :: (Serialize b, Monad m) => (b -> m b) -> b -> Auto m a b -- | The non-resuming/non-serializing version of iteratorM. iteratorM_ :: Monad m => (b -> m b) -> b -> Auto m a b -- | The stream of outputs is the cumulative/running sum of the inputs so -- far, starting with an initial count. -- -- The first output takes into account the first input. See -- sumFromD for a version where the first output is the initial -- count itself. -- --
-- sumFrom x0 = accum (+) x0 --sumFrom :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of sumFrom. sumFrom_ :: Num a => a -> Auto m a a -- | Like sumFrom, except the first output is the starting count. -- --
-- >>> let a = sumFromD 5 -- -- >>> let (y1, a') = stepAuto' a 10 -- -- >>> y1 -- 5 -- -- >>> let (y2, _ ) = stepAuto' a' 3 -- -- >>> y2 -- 10 ---- --
-- >>> streamAuto' (sumFrom 0) [1..10] -- [1,3,6,10,15,21,28,36,45,55] -- -- >>> streamAuto' (sumFromD 0) [1..10] -- [0,1,3,6,10,15,21,28,36,45] ---- -- It's sumFrom, but "delayed". -- -- Useful for recursive bindings, where you need at least one value to be -- able to produce its "first output" without depending on anything else. -- --
-- sumFromD x0 = sumFrom x0 . delay 0 ---- --
-- sumFromD x0 = delay x0 . sumFrom x0 --sumFromD :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of sumFromD. sumFromD_ :: Num a => a -> Auto m a a -- | The output is the running/cumulative product of all of the inputs so -- far, starting from an initial product. -- --
-- productFrom x0 = accum (*) x0 --productFrom :: (Serialize a, Num a) => a -> Auto m a a -- | The non-resuming/non-serializing version of productFrom. productFrom_ :: Num a => a -> Auto m a a -- | The output is the running/cumulative mconcat of all of the -- input seen so far, starting with mempty. -- --
-- >>> streamauto' mappender . map Last $ [Just 4, Nothing, Just 2, Just 3] -- [Last (Just 4), Last (Just 4), Last (Just 2), Last (Just 3)] -- -- >>> streamAuto' mappender ["hello","world","good","bye"] -- ["hello","helloworld","helloworldgood","helloworldgoodbye"] ---- --
-- mappender = accum mappend mempty --mappender :: (Serialize a, Monoid a) => Auto m a a -- | The non-resuming/non-serializing version of mappender. mappender_ :: Monoid a => Auto m a a -- | The output is the running <>-sum (mappend for -- Semigroup) of all of the input values so far, starting with a -- given starting value. Basically like mappender, but with a -- starting value. -- --
-- >>> streamAuto' (mappendFrom (Max 0)) [Max 4, Max (-2), Max 3, Max 10] -- [Max 4, Max 4, Max 4, Max 10] ---- --
-- mappendFrom m0 = accum (<>) m0 --mappendFrom :: (Serialize a, Semigroup a) => a -> Auto m a a -- | An Auto that returns the last value received by it. Given an -- "initial value" to output first. -- -- From the signal processing world, this is known as the "lag operator" -- L. -- -- This is (potentially) a very dangerous Auto, because its -- usage and its very existence opens the door to breaking -- denotative/declarative style and devolving into imperative style -- coding. However, when used where it is supposed to be used, it is more -- or less invaluable, and will be an essential part of many programs. -- -- Its main usage is for dealing with recursive bindings. If you ever are -- laying out recursive bindings in a high-level/denotative way, you need -- to have at least one value be able to have a "initial output" without -- depending on anything else. lastVal and delay allow you -- to do this. -- -- See the recursive example for more information on the -- appropriate usage of lastVal and delay. -- --
-- >>> streamAuto' (lastVal 100) [1..10] -- [100,1,2,3,4,5,6,7,8,9] --lastVal :: Serialize a => a -> Auto m a a -- | The non-resuming/non-serializing version of lastVal. lastVal_ :: a -> Auto m a a -- | An alias for lastVal; used in contexts where "delay" is more a -- meaningful description than "last value". All of the warnings for -- lastVal still apply, so you should probably read it if you -- haven't :) delay :: Serialize a => a -> Auto m a a -- | The non-resuming/non-serializing version of delay. delay_ :: a -> Auto m a a -- | A simple Auto that ignores all input; its output stream counts -- upwards from zero. -- --
-- >>> take 10 . streamAuto' count $ repeat () -- [0,1,2,3,4,5,6,7,8,9] --count :: (Serialize b, Num b) => Auto m a b -- | "This, then that". Behave like the first Interval (and run its -- effects) as long as it is "on" (outputting Just). As soon as it -- turns off (is Nothing), it'll "switch over" and begin behaving -- like the second Auto forever, running the effects of the second -- Auto, too. Works well if the Autos follow interval -- semantics from Control.Auto.Interval. -- --
-- >>> let a1 = whenI (<= 4) --> pure 0 -- -- >>> streamAuto' a1 [1..10] -- [1, 2, 3, 4, 0, 0, 0, 0, 0, 0] ---- -- (whenI only lets items satisfying the predicate pass through as -- "on", and is "off" otherwise; pure is the Auto that -- always produces the same output) -- -- Association works in a way that you can "chain" -->s, as -- long as you have an appropriate Auto (and not Interval) -- at the end: -- --
-- >>> let a2 = onFor 3 . sumFrom 0 -- --> onFor 3 . sumFrom 100 -- --> pure 0 -- -- >>> streamAuto' a2 [1..10] -- [1,3,6,104,109,115,0,0,0,0] ---- -- a --> b --> c associates as a --> (b --> -- c) -- -- This is pretty invaluable for having Autos "step" through a -- series of different Autos, progressing their state from one -- stage to the next. Autos can control when they want to be -- "moved on" from by turning "off" (outputting Nothing). -- -- Note that recursive bindings work just fine, so: -- --
-- >>> let a3 = onFor 2 . pure "hello" -- --> onFor 2 . pure "world" -- --> a3 -- -- >>> let (res3, _) = stepAutoN' 8 a3 () -- -- >>> res3 -- ["hello", "hello", "world", "world", "hello", "hello", "world", "world"] ---- -- the above represents an infinite loop between outputting "hello" and -- outputting "world". -- -- For serialization, an extra byte cost is incurred per invocation of -- -->. For cyclic switches like a3, every time the -- cycle "completes", it adds another layer of --> byte costs. -- For example, initially, saving a3 incurs a cost for the two -- -->s. After a3 loops once, it incurs a cost for -- another two -->s, so it costs four -->s. After -- a3 loops another time, it is like a cost of six -- -->s. So be aware that for cyclic bindings like a3, -- space for serialization grows at O(n). -- -- By the way, it might be worth contrasting this with <|!> -- and <|?> from Control.Auto.Interval, which have -- the same type signatures. Those alternative-y operators always feed -- the input to both sides, run both sides, and output the -- first Just. With <|!>, you can "switch back and -- forth" to the first Auto as soon as the first Auto is -- "on" (Just) again. -- -- -->, in contrast, runs only the first Auto -- until it is off (Nothing)...then runs only the second -- Auto. This transition is one-way, as well. (-->) :: Monad m => Interval m a b -> Auto m a b -> Auto m a b -- | A variation of -->, where the right hand side can also be an -- Interval / Maybe. The entire result is, then, a -- Maybe. Probably less useful than --> in most -- situations. (-?>) :: Monad m => Interval m a b -> Interval m a b -> Interval m a b -- | An Auto that runs every input through a a -> -- Maybe b test and produces a blip stream that emits the -- value inside every Just result. -- -- Particularly useful with prisms from the lens package, where -- things like emitJusts (preview _Right) will emit the -- b whenever the input Either a b stream is a -- Right. -- -- Warning! Carries all of the same dangers of emitOn. You can -- easily break blip semantics with this if you aren't sure what you are -- doing. Remember to only emit at discrete, separate occurences, and not -- for interval-like (on and off for chunks at a time) things. For -- interval semantics, we have Control.Auto.Interval. -- -- See the examples of emitOn for more concrete good/bad use -- cases. emitJusts :: (a -> Maybe b) -> Auto m a (Blip b) -- | Produces a blip stream that emits the input value whenever the input -- satisfies a given predicate. -- -- Warning! This Auto has the capability of "breaking" blip -- semantics. Be sure you know what you are doing when using this. Blip -- streams are semantically supposed to only emit at discrete, separate -- occurrences. Do not use this for interval-like (on and off for chunks -- at a time) things; each input should be dealt with as a separate -- thing. -- -- For interval semantics, we have Interval from -- Control.Auto.Interval. -- -- Good example: -- --
-- -- is only emitting at discrete blips -- emitOn even . iterator (+ 1) 0 ---- -- Bad examples: -- --
-- -- is emitting for "durations" or "intervals" of time. -- emitOn (< 10) . iterator (+ 1) 0 -- -- emitOn (const True) . foo ---- -- These bad examples would be good use cases of Interval. -- -- Can be particularly useful with prisms from the lens package, -- where things like emitOn (has _Right) and emitOn (hasn't -- _Right) will emit the input Either a b whenever it is or -- isn't a Right. See emitJusts for more common uses with -- lens. emitOn :: (a -> Bool) -> Auto m a (Blip a) -- | 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 -> Auto m (Blip a) a -- | fromBlipsWith d f is an Auto that decomposes -- the incoming blip stream by constantly outputting d except -- when the stream emits, and outputs the result of applying f -- to the emitted value when it does. fromBlipsWith :: b -> (a -> b) -> Auto m (Blip a) b -- | holdWith y0 is an Auto whose output is always -- the /most recently emitted/ value from the input blip stream. Before -- anything is emitted, y0 is outputted as a placeholder. -- -- Contrast with hold from Control.Auto.Interval. holdWith :: Serialize a => a -> Auto m (Blip a) a -- | A non-serializing/non-resumable version of holdWith. holdWith_ :: a -> Auto m (Blip a) a -- | Takes an Auto m a b (an Auto that turns -- incoming as into outputting bs) into an -- Auto m (Blip a) (Blip b); the original -- Auto is lifted to only be applied to emitted contents of a blip -- stream. -- -- When the stream emits, the original Auto is "stepped" with the -- emitted value; when it does not, it is paused and frozen until the -- next emission. -- --
-- >>> let sums = perBlip (sumFrom 0) -- -- >>> let blps = eachAt 2 [1,5,2] -- -- >>> take 8 . streamAuto' blps $ repeat () -- [NoBlip, Blip 1, NoBlip, Blip 5, NoBlip, Blip 2, NoBlip, NoBlip] -- -- >>> take 8 . streamAuto' (sums . blps) $ repeat () -- [NoBlip, Blip 1, NoBlip, Blip 6, NoBlip, Blip 8, NoBlip, NoBlip] --perBlip :: Monad m => Auto m a b -> Auto m (Blip a) (Blip b) -- | An Auto that ignores its input and produces a blip stream never -- emits. never :: Auto m a (Blip b) -- | Produces a blip stream that emits with the first received input value, -- and never again after that. -- -- Often used with pure: -- --
-- immediately . pure "Emit me!" ---- -- Or, in proc notation: -- --
-- blp <- immediately -< "Emit me!" ---- -- to get a blip stream that emits a given value (eg., "Emit me!") once -- and stops emitting ever again. -- --
-- >>> streamAuto' (immediately . pure "Emit me!") [1..5] -- [Blip "Emit Me!", NoBlip, NoBlip, NoBlip, NoBlip] --immediately :: Auto m a (Blip a) -- | For onFor n, the first n items in the output -- stream are always "on" (passing through with exactly the value of the -- corresponding input); for the rest, the output stream is always "off", -- suppressing all input values forevermore. -- -- If a number less than 0 is passed, 0 is used. onFor :: Int -> Interval m a a -- | Lifts an Auto m a b (transforming as -- into bs) into an Auto m (Maybe a) -- (Maybe b) (or, Interval m (Maybe a) -- b, transforming intervals of as into -- intervals of b. -- -- It does this by running the Auuto as normal when the input is -- "on", and freezing itbeing "off" when the input is off/. -- --
-- >>> let a1 = during (sumFrom 0) . onFor 2 . pure 1 -- -- >>> take 5 . streamAuto' a1 $ repeat () -- [Just 1, Just 2, Nothing, Nothing, Nothing] ---- --
-- >>> let a2 = during (sumFrom 0) . offFor 2 . pure 1 -- -- >>> take 5 . streamAuto' a2 $ repeat () -- [Nothing, Nothing, Just 1, Just 2, Just 3] ---- -- (Remember that pure x is the Auto that ignores -- its input and constantly just pumps out x at every step) -- -- Note the difference between putting the sumFrom "after" the -- offFor in the chain with during (like the previous -- example) and putting the sumFrom "before": -- --
-- >>> let a3 = offFor 2 . sumFrom 0 . pure 1 -- -- >>> take 5 . streamAuto' a3 $ repeat () -- [Nothing, Nothing, Just 3, Just 4, Just 5] ---- -- In the first case (with a2), the output of pure -- 1 was suppressed by offFor, and during -- (sumFrom 0) was only summing on the times that the 1's -- were "allowed through"...so it only "starts counting" on the third -- step. -- -- In the second case (with a3), the output of the -- pure 1 is never suppressed, and went straight into the -- sumFrom 0. sumFrom is always summing, the -- entire time. The final output of that sumFrom 0 is -- suppressed at the end with offFor 2. during :: Monad m => Auto m a b -> Interval m (Maybe a) b -- | The output stream is alwayas off, regardless of the input. -- -- Note that any monadic effects of the input Auto when composed -- with off are still executed, even though their result value is -- suppressed. -- --
-- off == pure Nothing --off :: Interval m a b -- | The output stream is always on, with exactly the value of the -- corresponding input. -- --
-- toOn == arr Just --toOn :: Interval m a a -- | An "interval collapsing" Auto. A stream of on/off values comes -- in; the output is the value of the input when the input is on, and the -- "default value" when the input is off. -- -- Much like fromMaybe from Data.Maybe. -- --
-- fromInterval d = arr (fromMaybe d) --fromInterval :: a -> Auto m (Maybe a) a -- | 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. interactAuto :: Interval' String String -> IO (Interval' String String) -- | 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. interactRS :: (Read a, Show b) => Interval' a b -> IO (Interval' String String) -- | 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. streamAutoEffects :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> [a] -> Auto m' a b -> t m b -- | 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. toEffectStream :: (Monad m, MonadTrans t, MonadPlus (t m), Monad m') => (forall c. m' c -> m c) -> m a -> Auto m' a b -> t m b