-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/


-- | Beautiful Streaming, Concurrent and Reactive Composition
--   
--   Streamly is a monad transformer unifying non-determinism
--   (<a>list-t</a>/<a>logict</a>), concurrency (<a>async</a>), streaming
--   (<a>conduit</a>/<a>pipes</a>), and FRP (<a>Yampa</a>/<a>reflex</a>)
--   functionality in a concise and intuitive API. High level concurrency
--   makes concurrent applications almost indistinguishable from
--   non-concurrent ones. By changing a single combinator you can control
--   whether the code runs serially or concurrently. It naturally
--   integrates concurrency with streaming rather than adding it as an
--   afterthought. Moreover, it interworks with the popular streaming
--   libraries.
--   
--   See the README for an overview and the haddock documentation for full
--   reference. It is recommended to read the comprehensive tutorial module
--   <a>Streamly.Tutorial</a> first. Also see <a>Streamly.Examples</a> for
--   some working examples.
@package streamly
@version 0.1.2


module Streamly.Prelude

-- | Represesnts an empty stream just like <tt>[]</tt> represents an empty
--   list.
nil :: Streaming t => t m a

-- | Constructs a stream by adding a pure value at the head of an existing
--   stream, just like <tt>:</tt> constructs lists. For example:
--   
--   <pre>
--   &gt; let stream = 1 `cons` 2 `cons` 3 `cons` nil
--   &gt; (toList . serially) stream
--   [1,2,3]
--   </pre>
cons :: (Streaming t) => a -> t m a -> t m a
infixr 5 `cons`

-- | Operator equivalent of <a>cons</a> so that you can construct a stream
--   of pure values more succinctly like this:
--   
--   <pre>
--   &gt; let stream = 1 .: 2 .: 3 .: nil
--   &gt; (toList . serially) stream
--   [1,2,3]
--   </pre>
--   
--   <a>.:</a> constructs a stream just like <tt>:</tt> constructs a list.
--   
--   Also note that another equivalent way of building streams from pure
--   values is:
--   
--   <pre>
--   &gt; let stream = pure 1 &lt;&gt; pure 2 &lt;&gt; pure 3
--   &gt; (toList . serially) stream
--   [1,2,3]
--   </pre>
--   
--   In the first method we construct a stream by adding one element at a
--   time. In the second method we first construct singleton streams using
--   <a>pure</a> and then compose all those streams together using the
--   <a>Semigroup</a> style composition of streams. The former method is a
--   bit more efficient than the latter.
(.:) :: (Streaming t) => a -> t m a -> t m a
infixr 5 .:

-- | Build a Stream by unfolding pure steps starting from a seed.
unfoldr :: Streaming t => (b -> Maybe (a, b)) -> b -> t m a

-- | Build a Stream by unfolding monadic steps starting from a seed.
unfoldrM :: (Streaming t, Monad m) => (b -> m (Maybe (a, b))) -> b -> t m a

-- | Same as <tt>foldWith (&lt;&gt;)</tt> but more efficient.
each :: (Streaming t, Foldable f) => f a -> t m a

-- | Iterate a pure function from a seed value, streaming the results
--   forever
iterate :: Streaming t => (a -> a) -> a -> t m a

-- | Iterate a monadic function from a seed value, streaming the results
--   forever
iterateM :: (Streaming t, Monad m) => (a -> m a) -> a -> t m a

-- | Right fold.
foldr :: (Streaming t, Monad m) => (a -> b -> b) -> b -> t m a -> m b

-- | Right fold with a monadic step function. See <a>toList</a> for an
--   example use.
foldrM :: Streaming t => (a -> m b -> m b) -> m b -> t m a -> m b

-- | Scan left. A strict left fold which accumulates the result of its
--   reduction steps inside a stream, from left.
scan :: Streaming t => (x -> a -> x) -> x -> (x -> b) -> t m a -> t m b

-- | Strict left fold. This is typed to work with the foldl package. To use
--   it normally just pass <a>id</a> as the third argument.
foldl :: (Streaming t, Monad m) => (x -> a -> x) -> x -> (x -> b) -> t m a -> m b

-- | Strict left fold, with monadic step function. This is typed to work
--   with the foldl package. To use directly pass <a>id</a> as the third
--   argument.
foldlM :: (Streaming t, Monad m) => (x -> a -> m x) -> m x -> (x -> m b) -> t m a -> m b

-- | Decompose a stream into its head and tail. If the stream is empty,
--   returns <a>Nothing</a>. If the stream is non-empty, returns 'Just (a,
--   ma)', where <tt>a</tt> is the head of the stream and <tt>ma</tt> its
--   tail.
uncons :: (Streaming t, Monad m) => t m a -> m (Maybe (a, t m a))

-- | Convert a stream into a list in the underlying monad.
toList :: (Streaming t, Monad m) => t m a -> m [a]

-- | Determine whether all elements of a stream satisfy a predicate.
all :: (Streaming t, Monad m) => (a -> Bool) -> t m a -> m Bool

-- | Determine whether any of the elements of a stream satisfy a predicate.
any :: (Streaming t, Monad m) => (a -> Bool) -> t m a -> m Bool

-- | Extract the first element of the stream, if any.
head :: (Streaming t, Monad m) => t m a -> m (Maybe a)

-- | Extract all but the first element of the stream, if any.
tail :: (Streaming t, Monad m) => t m a -> m (Maybe (t m a))

-- | Extract the last element of the stream, if any.
last :: (Streaming t, Monad m) => t m a -> m (Maybe a)

-- | Determine whether the stream is empty.
null :: (Streaming t, Monad m) => t m a -> m Bool

-- | Determine the length of the stream.
length :: (Streaming t, Monad m) => t m a -> m Int

-- | Determine whether an element is present in the stream.
elem :: (Streaming t, Monad m, Eq a) => a -> t m a -> m Bool

-- | Determine whether an element is not present in the stream.
notElem :: (Streaming t, Monad m, Eq a) => a -> t m a -> m Bool

-- | Returns the elements of the stream in reverse order. The stream must
--   be finite.
reverse :: (Streaming t) => t m a -> t m a

-- | Determine the maximum element in a stream.
maximum :: (Streaming t, Monad m, Ord a) => t m a -> m (Maybe a)

-- | Determine the minimum element in a stream.
minimum :: (Streaming t, Monad m, Ord a) => t m a -> m (Maybe a)

-- | Determine the sum of all elements of a stream of numbers
sum :: (Streaming t, Monad m, Num a) => t m a -> m a

-- | Determine the product of all elements of a stream of numbers
product :: (Streaming t, Monad m, Num a) => t m a -> m a

-- | Include only those elements that pass a predicate.
filter :: Streaming t => (a -> Bool) -> t m a -> t m a

-- | Take first <tt>n</tt> elements from the stream and discard the rest.
take :: Streaming t => Int -> t m a -> t m a

-- | End the stream as soon as the predicate fails on an element.
takeWhile :: Streaming t => (a -> Bool) -> t m a -> t m a

-- | Discard first <tt>n</tt> elements from the stream and take the rest.
drop :: Streaming t => Int -> t m a -> t m a

-- | Drop elements in the stream as long as the predicate succeeds and then
--   take the rest of the stream.
dropWhile :: Streaming t => (a -> Bool) -> t m a -> t m a

-- | Replace each element of the stream with the result of a monadic action
--   applied on the element.
mapM :: (Streaming t, Monad m) => (a -> m b) -> t m a -> t m b

-- | Apply a monadic action to each element of the stream and discard the
--   output of the action.
mapM_ :: (Streaming t, Monad m) => (a -> m b) -> t m a -> m ()

-- | Reduce a stream of monadic actions to a stream of the output of those
--   actions.
sequence :: (Streaming t, Monad m) => t m (m a) -> t m a

-- | Generate a stream by performing an action <tt>n</tt> times.
replicateM :: (Streaming t, Monad m) => Int -> m a -> t m a

-- | Zip two streams serially using a pure zipping function.
zipWith :: Streaming t => (a -> b -> c) -> t m a -> t m b -> t m c

-- | Zip two streams serially using a monadic zipping function.
zipWithM :: Streaming t => (a -> b -> t m c) -> t m a -> t m b -> t m c

-- | Zip two streams asyncly (i.e. both the elements being zipped are
--   generated concurrently) using a pure zipping function.
zipAsyncWith :: (Streaming t, MonadAsync m) => (a -> b -> c) -> t m a -> t m b -> t m c

-- | Zip two streams asyncly (i.e. both the elements being zipped are
--   generated concurrently) using a monadic zipping function.
zipAsyncWithM :: (Streaming t, MonadAsync m) => (a -> b -> t m c) -> t m a -> t m b -> t m c

-- | Read lines from an IO Handle into a stream of Strings.
fromHandle :: (Streaming t, MonadIO m) => Handle -> t m String

-- | Write a stream of Strings to an IO Handle.
toHandle :: (Streaming t, MonadIO m) => Handle -> t m String -> m ()


module Streamly

-- | A monad that can perform asynchronous/concurrent IO operations.
--   Streams that can be composed concurrently require the underlying monad
--   to be <a>MonadAsync</a>.
type MonadAsync m = (MonadIO m, MonadBaseControl IO m, MonadThrow m)

-- | Class of types that can represent a stream of elements of some type
--   <tt>a</tt> in some monad <tt>m</tt>.
class Streaming t

-- | The <a>Monad</a> instance of <a>StreamT</a> runs the <i>monadic
--   continuation</i> for each element of the stream, serially.
--   
--   <pre>
--   main = <a>runStreamT</a> $ do
--       x &lt;- return 1 &lt;&gt; return 2
--       liftIO $ print x
--   </pre>
--   
--   <pre>
--   1
--   2
--   </pre>
--   
--   <a>StreamT</a> nests streams serially in a depth first manner.
--   
--   <pre>
--   main = <a>runStreamT</a> $ do
--       x &lt;- return 1 &lt;&gt; return 2
--       y &lt;- return 3 &lt;&gt; return 4
--       liftIO $ print (x, y)
--   </pre>
--   
--   <pre>
--   (1,3)
--   (1,4)
--   (2,3)
--   (2,4)
--   </pre>
--   
--   This behavior is exactly like a list transformer. We call the monadic
--   code being run for each element of the stream a monadic continuation.
--   In imperative paradigm we can think of this composition as nested
--   <tt>for</tt> loops and the monadic continuation is the body of the
--   loop. The loop iterates for all elements of the stream.
data StreamT m a

-- | Like <a>StreamT</a> but different in nesting behavior. It fairly
--   interleaves the iterations of the inner and the outer loop, nesting
--   loops in a breadth first manner.
--   
--   <pre>
--   main = <a>runInterleavedT</a> $ do
--       x &lt;- return 1 &lt;&gt; return 2
--       y &lt;- return 3 &lt;&gt; return 4
--       liftIO $ print (x, y)
--   </pre>
--   
--   <pre>
--   (1,3)
--   (2,3)
--   (1,4)
--   (2,4)
--   </pre>
data InterleavedT m a

-- | Like <a>StreamT</a> but <i>may</i> run each iteration concurrently
--   using demand driven concurrency. More concurrent iterations are
--   started only if the previous iterations are not able to produce enough
--   output for the consumer.
--   
--   <pre>
--   import <a>Streamly</a>
--   import Control.Concurrent
--   
--   main = <a>runAsyncT</a> $ do
--       n &lt;- return 3 &lt;&gt; return 2 &lt;&gt; return 1
--       liftIO $ do
--            threadDelay (n * 1000000)
--            myThreadId &gt;&gt;= \tid -&gt; putStrLn (show tid ++ ": Delay " ++ show n)
--   </pre>
--   
--   <pre>
--   ThreadId 40: Delay 1
--   ThreadId 39: Delay 2
--   ThreadId 38: Delay 3
--   </pre>
--   
--   All iterations may run in the same thread if they do not block.
data AsyncT m a

-- | Like <a>StreamT</a> but runs <i>all</i> iterations fairly concurrently
--   using a round robin scheduling.
--   
--   <pre>
--   import <a>Streamly</a>
--   import Control.Concurrent
--   
--   main = <a>runParallelT</a> $ do
--       n &lt;- return 3 &lt;&gt; return 2 &lt;&gt; return 1
--       liftIO $ do
--            threadDelay (n * 1000000)
--            myThreadId &gt;&gt;= \tid -&gt; putStrLn (show tid ++ ": Delay " ++ show n)
--   </pre>
--   
--   <pre>
--   ThreadId 40: Delay 1
--   ThreadId 39: Delay 2
--   ThreadId 38: Delay 3
--   </pre>
--   
--   Unlike <a>AsyncT</a> all iterations are guaranteed to run fairly
--   concurrently, unconditionally.
data ParallelT m a

-- | <a>ZipStream</a> zips serially i.e. it produces one element from each
--   stream serially and then zips the two elements. Note, for convenience
--   we have used the <a>zipping</a> combinator in the following example
--   instead of using a type annotation.
--   
--   <pre>
--   main = (toList . <a>zipping</a> $ (,) &lt;$&gt; s1 &lt;*&gt; s2) &gt;&gt;= print
--       where s1 = pure 1 &lt;&gt; pure 2
--             s2 = pure 3 &lt;&gt; pure 4
--   </pre>
--   
--   <pre>
--   [(1,3),(2,4)]
--   </pre>
--   
--   This applicative operation can be seen as the zipping equivalent of
--   interleaving with <a>&lt;=&gt;</a>.
data ZipStream m a

-- | Like <a>ZipStream</a> but zips in parallel, it generates both the
--   elements to be zipped concurrently.
--   
--   <pre>
--   main = (toList . <a>zippingAsync</a> $ (,) &lt;$&gt; s1 &lt;*&gt; s2) &gt;&gt;= print
--       where s1 = pure 1 &lt;&gt; pure 2
--             s2 = pure 3 &lt;&gt; pure 4
--   </pre>
--   
--   <pre>
--   [(1,3),(2,4)]
--   </pre>
--   
--   This applicative operation can be seen as the zipping equivalent of
--   parallel composition with <a>&lt;|&gt;</a>.
data ZipAsync m a

-- | Sequential interleaved composition, in contrast to <a>&lt;&gt;</a>
--   this operator fairly interleaves two streams instead of appending
--   them; yielding one element from each stream alternately.
--   
--   <pre>
--   main = (<tt>toList</tt> . <a>serially</a> $ (return 1 &lt;&gt; return 2) &lt;=&gt; (return 3 &lt;&gt; return 4)) &gt;&gt;= print
--   </pre>
--   
--   <pre>
--   [1,3,2,4]
--   </pre>
--   
--   This operator corresponds to the <a>InterleavedT</a> style. Unlike
--   <a>&lt;&gt;</a>, this operator cannot be used to fold infinite
--   containers since that might accumulate too many partially drained
--   streams. To be clear, it can combine infinite streams but not infinite
--   number of streams.
(<=>) :: Streaming t => t m a -> t m a -> t m a
infixr 5 <=>

-- | Demand driven concurrent composition. In contrast to <a>&lt;|&gt;</a>
--   this operator concurrently "merges" streams in a left biased manner
--   rather than fairly interleaving them. It keeps yielding from the
--   stream on the left as long as it can. If the left stream blocks or
--   cannot keep up with the pace of the consumer it can concurrently yield
--   from the stream on the right in parallel.
--   
--   <pre>
--   main = (<tt>toList</tt> . <a>serially</a> $ (return 1 &lt;&gt; return 2) &lt;| (return 3 &lt;&gt; return 4)) &gt;&gt;= print
--   </pre>
--   
--   <pre>
--   [1,2,3,4]
--   </pre>
--   
--   Unlike <a>&lt;|&gt;</a> it can be used to fold infinite containers of
--   streams. This operator corresponds to the <a>AsyncT</a> type for
--   product style composition.
(<|) :: (Streaming t, MonadAsync m) => t m a -> t m a -> t m a

-- | Make a stream asynchronous, triggers the computation and returns a
--   stream in the underlying monad representing the output generated by
--   the original computation. The returned action is exhaustible and must
--   be drained once. If not drained fully we may have a thread blocked
--   forever and once exhausted it will always return <a>empty</a>.
async :: (Streaming t, MonadAsync m) => t m a -> m (t m a)

-- | Interpret an ambiguously typed stream as <a>StreamT</a>.
serially :: StreamT m a -> StreamT m a

-- | Interpret an ambiguously typed stream as <a>InterleavedT</a>.
interleaving :: InterleavedT m a -> InterleavedT m a

-- | Interpret an ambiguously typed stream as <a>AsyncT</a>.
asyncly :: AsyncT m a -> AsyncT m a

-- | Interpret an ambiguously typed stream as <a>ParallelT</a>.
parallely :: ParallelT m a -> ParallelT m a

-- | Interpret an ambiguously typed stream as <a>ZipStream</a>.
zipping :: ZipStream m a -> ZipStream m a

-- | Interpret an ambiguously typed stream as <a>ZipAsync</a>.
zippingAsync :: ZipAsync m a -> ZipAsync m a

-- | Adapt one streaming type to another.
adapt :: (Streaming t1, Streaming t2) => t1 m a -> t2 m a

-- | Run a streaming composition, discard the results.
runStreaming :: (Monad m, Streaming t) => t m a -> m ()

-- | Same as <tt>runStreaming . serially</tt>.
runStreamT :: Monad m => StreamT m a -> m ()

-- | Same as <tt>runStreaming . interleaving</tt>.
runInterleavedT :: Monad m => InterleavedT m a -> m ()

-- | Same as <tt>runStreaming . asyncly</tt>.
runAsyncT :: Monad m => AsyncT m a -> m ()

-- | Same as <tt>runStreaming . parallely</tt>.
runParallelT :: Monad m => ParallelT m a -> m ()

-- | Same as <tt>runStreaming . zipping</tt>.
runZipStream :: Monad m => ZipStream m a -> m ()

-- | Same as <tt>runStreaming . zippingAsync</tt>.
runZipAsync :: Monad m => ZipAsync m a -> m ()

-- | Like the <tt>Prelude</tt> <tt>fold</tt> but allows you to specify a
--   binary sum style stream composition operator to fold a container of
--   streams.
--   
--   <pre>
--   foldWith (&lt;&gt;) $ map return [1..3]
--   </pre>
foldWith :: (Streaming t, Foldable f) => (t m a -> t m a -> t m a) -> f (t m a) -> t m a

-- | Like <a>foldMap</a> but allows you to specify a binary sum style
--   composition operator to fold a container of streams. Maps a monadic
--   streaming action on the container before folding it.
--   
--   <pre>
--   foldMapWith (&lt;&gt;) return [1..3]
--   </pre>
foldMapWith :: (Streaming t, Foldable f) => (t m b -> t m b -> t m b) -> (a -> t m b) -> f a -> t m b

-- | Like <a>foldMapWith</a> but with the last two arguments reversed i.e.
--   the monadic streaming function is the last argument.
forEachWith :: (Streaming t, Foldable f) => (t m b -> t m b -> t m b) -> f a -> (a -> t m b) -> t m b

-- | The class of monoids (types with an associative binary operation that
--   has an identity). Instances should satisfy the following laws:
--   
--   <ul>
--   <li><pre>mappend mempty x = x</pre></li>
--   <li><pre>mappend x mempty = x</pre></li>
--   <li><pre>mappend x (mappend y z) = mappend (mappend x y) z</pre></li>
--   <li><pre>mconcat = <a>foldr</a> mappend mempty</pre></li>
--   </ul>
--   
--   The method names refer to the monoid of lists under concatenation, but
--   there are many other instances.
--   
--   Some types can be viewed as a monoid in more than one way, e.g. both
--   addition and multiplication on numbers. In such cases we often define
--   <tt>newtype</tt>s and make those instances of <a>Monoid</a>, e.g.
--   <tt>Sum</tt> and <tt>Product</tt>.
class Monoid a

-- | Identity of <a>mappend</a>
mempty :: Monoid a => a

-- | An associative operation
mappend :: Monoid a => a -> a -> a

-- | Fold a list using the monoid. For most types, the default definition
--   for <a>mconcat</a> will be used, but the function is included in the
--   class definition so that an optimized version can be provided for
--   specific types.
mconcat :: Monoid a => [a] -> a

-- | The class of semigroups (types with an associative binary operation).
class Semigroup a

-- | An associative operation.
--   
--   <pre>
--   (a <a>&lt;&gt;</a> b) <a>&lt;&gt;</a> c = a <a>&lt;&gt;</a> (b <a>&lt;&gt;</a> c)
--   </pre>
--   
--   If <tt>a</tt> is also a <a>Monoid</a> we further require
--   
--   <pre>
--   (<a>&lt;&gt;</a>) = <a>mappend</a>
--   </pre>
(<>) :: Semigroup a => a -> a -> a

-- | Reduce a non-empty list with <tt>&lt;&gt;</tt>
--   
--   The default definition should be sufficient, but this can be
--   overridden for efficiency.
sconcat :: Semigroup a => NonEmpty a -> a

-- | Repeat a value <tt>n</tt> times.
--   
--   Given that this works on a <a>Semigroup</a> it is allowed to fail if
--   you request 0 or fewer repetitions, and the default definition will do
--   so.
--   
--   By making this a member of the class, idempotent semigroups and
--   monoids can upgrade this to execute in <i>O(1)</i> by picking
--   <tt>stimes = stimesIdempotent</tt> or <tt>stimes =
--   stimesIdempotentMonoid</tt> respectively.
stimes :: (Semigroup a, Integral b) => b -> a -> a

-- | A monoid on applicative functors.
--   
--   If defined, <a>some</a> and <a>many</a> should be the least solutions
--   of the equations:
--   
--   <ul>
--   <li><pre>some v = (:) <tt>&lt;$&gt;</tt> v <a>&lt;*&gt;</a> many
--   v</pre></li>
--   <li><pre>many v = some v <a>&lt;|&gt;</a> <a>pure</a> []</pre></li>
--   </ul>
class Applicative f => Alternative (f :: * -> *)

-- | The identity of <a>&lt;|&gt;</a>
empty :: Alternative f => f a

-- | An associative binary operation
(<|>) :: Alternative f => f a -> f a -> f a

-- | One or more.
some :: Alternative f => f a -> f [a]

-- | Zero or more.
many :: Alternative f => f a -> f [a]

-- | Monads that also support choice and failure.
class (Alternative m, Monad m) => MonadPlus (m :: * -> *)

-- | the identity of <a>mplus</a>. It should also satisfy the equations
--   
--   <pre>
--   mzero &gt;&gt;= f  =  mzero
--   v &gt;&gt; mzero   =  mzero
--   </pre>
mzero :: MonadPlus m => m a

-- | an associative operation
mplus :: MonadPlus m => m a -> m a -> m a

-- | Monads in which <a>IO</a> computations may be embedded. Any monad
--   built by applying a sequence of monad transformers to the <a>IO</a>
--   monad will be an instance of this class.
--   
--   Instances should satisfy the following laws, which state that
--   <a>liftIO</a> is a transformer of monads:
--   
--   <ul>
--   <li><pre><a>liftIO</a> . <a>return</a> = <a>return</a></pre></li>
--   <li><pre><a>liftIO</a> (m &gt;&gt;= f) = <a>liftIO</a> m &gt;&gt;=
--   (<a>liftIO</a> . f)</pre></li>
--   </ul>
class Monad m => MonadIO (m :: * -> *)

-- | Lift a computation from the <a>IO</a> monad.
liftIO :: MonadIO m => IO a -> m a

-- | The class of monad transformers. Instances should satisfy the
--   following laws, which state that <a>lift</a> is a monad
--   transformation:
--   
--   <ul>
--   <li><pre><a>lift</a> . <a>return</a> = <a>return</a></pre></li>
--   <li><pre><a>lift</a> (m &gt;&gt;= f) = <a>lift</a> m &gt;&gt;=
--   (<a>lift</a> . f)</pre></li>
--   </ul>
class MonadTrans (t :: (* -> *) -> * -> *)

-- | Lift a computation from the argument monad to the constructed monad.
lift :: (MonadTrans t, Monad m) => m a -> t m a


-- | Time utilities for reactive programming.
module Streamly.Time

-- | Run an action forever periodically at the given frequency specified in
--   per second (Hz).
periodic :: Int -> IO () -> IO ()

-- | Run a computation on every clock tick, the clock runs at the specified
--   frequency. It allows running a computation at high frequency
--   efficiently by maintaining a local clock and adjusting it with the
--   provided base clock at longer intervals. The first argument is a base
--   clock returning some notion of time in microseconds. The second
--   argument is the frequency in per second (Hz). The third argument is
--   the action to run, the action is provided the local time as an
--   argument.
withClock :: IO Int -> Int -> (Int -> IO ()) -> IO ()


-- | Streamly, short for stream concurrently, combines the essence of
--   non-determinism, streaming and concurrency in functional programming.
--   Concurrent and non-concurrent applications are almost
--   indistinguisable, concurrency capability does not at all impact the
--   performance of non-concurrent case. Streaming enables writing modular,
--   composable and scalable applications with ease and concurrency allows
--   you to make them scale and perform well. Streamly enables writing
--   concurrent applications without being aware of threads or
--   synchronization. No explicit thread control is needed, where
--   applicable the concurrency rate is automatically controlled based on
--   the demand by the consumer. However, combinators are provided to fine
--   tune the concurrency control. Streaming and concurrency together
--   enable expressing reactive applications conveniently. See
--   <a>Streamly.Examples</a> for a simple SDL based FRP example.
--   
--   Streamly streams are very much like the Haskell lists and most of the
--   functions that work on lists have a counterpart that works on streams.
--   However, streamly streams can be generated, consumed or combined
--   concurrently. In this tutorial we will go over the basic concepts and
--   how to use the library. The documentation of <tt>Streamly</tt> module
--   has more details on core APIs. For more APIs for constructing,
--   folding, filtering, mapping and zipping etc. see the documentation of
--   <a>Streamly.Prelude</a> module. For examples and other ways to use the
--   library see the module <a>Streamly.Examples</a> as well.
module Streamly.Tutorial
