{-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Control.Auto.Generate -- Description : 'Auto's that act as generators or "producers", ignoring input. -- Copyright : (c) Justin Le 2015 -- License : MIT -- Maintainer : justin@jle.im -- Stability : unstable -- Portability : portable -- -- This module contains various 'Auto's 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 ( -- * From lists fromList , fromList_ , fromLongList -- * Constant producers -- $constant , pure , effect -- * From functions -- ** Iterating , iterator , iterator_ , iteratorM , iteratorM_ -- ** Enumerating results of a function , discreteF , discreteF_ -- ** Unfolding -- | "Iterating with state". , unfold , unfold_ , unfoldM , unfoldM_ -- * Enumerating , enumFromA , enumFromA_ ) where import Control.Applicative import Control.Auto.Core import Control.Auto.Interval import Control.Category import Data.Serialize import Prelude hiding ((.), id) -- | 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'. -- -- * Storing: O(n) time and space on length of remaining list -- * Loading: O(1) time in the number of times the 'Auto' has been -- stepped + O(n) time in the length of the remaining list. -- fromList :: Serialize b => [b] -- ^ list to output element-by-element -> Interval m a b fromList = mkState (const _uncons) -- | A version of 'fromList' that is safe for long or infinite lists, or -- lists with unserializable elements. -- -- There is a small cost in the time of loading/resuming, which is @O(n)@ -- on the number of times the Auto had been stepped at the time of -- saving. This is because it has to drop the @n@ first elements in the -- list, to "resume" to the proper position. -- -- * Storing: O(1) time and space on the length of the remaining list -- * Loading: O(n) time on the number of times the 'Auto' has been -- stepped, maxing out at O(n) on the length of the entire input list. -- fromLongList :: [b] -- ^ list to output element-by-element -> Interval m a b fromLongList xs = go 0 xs where loader = do stopped <- get if stopped then return finished else do i <- get return (go i (drop i xs)) finished = mkAuto loader (put True) (const (Nothing, finished)) go i ys = mkAuto loader (put (False, i)) $ \_ -> case ys of (y':ys') -> (Just y', go (i + 1) ys') [] -> (Nothing, finished) -- | The non-resuming/non-serializing version of 'fromList'. fromList_ :: [b] -- ^ list to output element-by-element -> Interval m a b fromList_ = mkState_ (const _uncons) _uncons :: [a] -> (Maybe a, [a]) _uncons [] = (Nothing, []) _uncons (x:xs) = (Just x , xs) -- | 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)) -- ^ unfolding function -> c -- ^ initial accumulator -> Interval m a b unfold f = mkState (_unfoldF f) . Just -- | Like 'unfold', but the unfolding function is monadic. unfoldM :: (Serialize c, Monad m) => (c -> m (Maybe (b, c))) -- ^ unfolding function -> c -- ^ initial accumulator -> Interval m a b unfoldM f = mkStateM (_unfoldMF f) . Just -- | The non-resuming & non-serializing version of 'unfold'. unfold_ :: (c -> Maybe (b, c)) -- ^ unfolding function -> c -- ^ initial accumulator -> Interval m a b unfold_ f = mkState_ (_unfoldF f) . Just -- | The non-resuming & non-serializing version of 'unfoldM'. unfoldM_ :: Monad m => (c -> m (Maybe (b, c))) -- ^ unfolding function -> c -- ^ initial accumulator -> Interval m a b unfoldM_ f = mkStateM_ (_unfoldMF f) . Just _unfoldF :: (c -> Maybe (b, c)) -> a -> Maybe c -> (Maybe b, Maybe c) _unfoldF _ _ Nothing = (Nothing, Nothing) _unfoldF f _ (Just x) = case f x of Just (y, x') -> (Just y, Just x') Nothing -> (Nothing, Nothing) _unfoldMF :: Monad m => (c -> m (Maybe (b, c))) -> a -> Maybe c -> m (Maybe b, Maybe c) _unfoldMF _ _ Nothing = return (Nothing, Nothing) _unfoldMF f _ (Just x) = do res <- f x return $ case res of Just (y, x') -> (Just y, Just x') Nothing -> (Nothing, Nothing) -- | 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) -- ^ iterating function -> b -- ^ starting value and initial output -> Auto m a b iterator f = accumD (\x _ -> f x) -- | Like 'iterator', but with a monadic function. iteratorM :: (Serialize b, Monad m) => (b -> m b) -- ^ (monadic) iterating function -> b -- ^ starting value and initial output -> Auto m a b iteratorM f = accumMD (\x _ -> f x) -- | The non-resuming/non-serializing version of 'iterator'. iterator_ :: (b -> b) -- ^ iterating function -> b -- ^ starting value and initial output -> Auto m a b iterator_ f = accumD_ (\x _ -> f x) -- | The non-resuming/non-serializing version of 'iteratorM'. iteratorM_ :: Monad m => (b -> m b) -- ^ (monadic) iterating function -> b -- ^ starting value and initial output -> Auto m a b iteratorM_ f = accumMD_ (\x _ -> f x) -- | Continually enumerate from the starting value, using `succ`. enumFromA :: (Serialize b, Enum b) => b -- ^ initial value -> Auto m a b enumFromA = iterator succ -- | The non-serializing/non-resuming version of `enumFromA`. enumFromA_ :: Enum b => b -- ^ initial value -> Auto m a b enumFromA_ = iterator_ succ -- | 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) -- ^ discrete function -> c -- ^ initial input -> Auto m a b discreteF f = mkState $ \_ x -> (f x, succ x) -- | The non-resuming/non-serializing version of `discreteF`. discreteF_ :: Enum c => (c -> b) -- ^ discrete function -> c -- ^ initial input -> Auto m a b discreteF_ f = mkState_ $ \_ x -> (f x, succ x) -- $constant -- -- Here we have the "constant producers": 'Auto's whose output is always -- the same value, or the result of executing the same monadic action. -- -- @ -- 'pure' :: 'Monad' m => b -> 'Auto' m a b -- 'effect' :: 'Monad' m => m b -> 'Auto' m a b -- @ -- -- 'pure' always outputs the same value, ignoring its input, and 'effect' -- always outputs the result of executing the same monadic action, ignoring -- its input. -- | 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 -- -- Our '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 -- ^ monadic action to contually execute. -> Auto m a b effect = mkConstM {-# INLINE effect #-}