on master help sign in
Move old README to docs/Overview.md
commitdf676c66035e18859fe65c48d95550c323f8225a authorHarendra Kumar <harendra@composewell.com> dateThu 20 May 2021, 10:44 (5 years ago) parent973e563
2 files changed+607
docs/Overview.md+606 streamly.cabal+1
docs/Overview.mdnew file+606
@@ -0,0 +1,606 @@
1+# Streamly
2+
3+[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
4+[![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly)
5+[![Travis](https://travis-ci.com/composewell/streamly.svg?branch=master)](https://travis-ci.com/composewell/streamly)
6+[![Appveyor](https://ci.appveyor.com/api/projects/status/ajxg0c79raou9ned?svg=true)](https://ci.appveyor.com/project/harendra-kumar/streamly)
7+[![CircleCI](https://circleci.com/gh/composewell/streamly/tree/master.svg?style=svg)](https://circleci.com/gh/composewell/streamly/tree/master)
8+[![Coverage Status](https://coveralls.io/repos/composewell/streamly/badge.svg?branch=master&service=github)](https://coveralls.io/github/composewell/streamly?branch=master)
9+
10+## Learning Materials
11+
12+* Documentation: [Quick](#streaming-concurrently) | [Tutorial](https://hackage.haskell.org/package/streamly/docs/Streamly-Tutorial.html) | [Reference (Hackage)](https://hackage.haskell.org/package/streamly) | [Reference (Latest)](https://composewell.github.io/streamly) | [Guides](docs)
13+* Installing: [Installing](./INSTALL.md) | [Building for optimal performance](docs/Build.md)
14+* Examples: [streamly](examples) | [streamly-examples](https://github.com/composewell/streamly-examples)
15+* Benchmarks: [Streaming](https://github.com/composewell/streaming-benchmarks) | [Concurrency](https://github.com/composewell/concurrency-benchmarks)
16+* Talks: [Functional Conf 2019 Video](https://www.youtube.com/watch?v=uzsqgdMMgtk) | [Functional Conf 2019 Slides](https://www.slideshare.net/HarendraKumar10/streamly-concurrent-data-flow-programming)
17+
18+## Streaming Concurrently
19+
20+Haskell lists express pure computations using composable stream operations like
21+`:`, `unfold`, `map`, `filter`, `zip` and `fold`. Streamly is exactly like
22+lists except that it can express sequences of pure as well as monadic
23+computations aka streams. More importantly, it can express monadic sequences
24+with concurrent execution semantics without introducing any additional APIs.
25+
26+Streamly expresses concurrency using standard, well known abstractions.
27+Concurrency semantics are defined for list operations, semigroup, applicative
28+and monadic compositions. Programmer does not need to know any low level
29+notions of concurrency like threads, locking or synchronization. Concurrent
30+and non-concurrent programs are fundamentally the same. A chosen segment of
31+the program can be made concurrent by annotating it with an appropriate
32+combinator. We can choose a combinator for lookahead style or asynchronous
33+concurrency. Concurrency is automatically scaled up or down based on the
34+demand from the consumer application, we can finally say goodbye to managing
35+thread pools and associated sizing issues. The result is truly fearless
36+and declarative monadic concurrency.
37+
38+## Where to use streamly?
39+
40+Streamly is a general purpose programming framework. It can be used equally
41+efficiently from a simple `Hello World!` program to a massively concurrent
42+application. The answer to the question, "where to use streamly?" - would be
43+similar to the answer to - "Where to use Haskell lists or the IO monad?".
44+
45+Streamly simplifies streaming and makes it as intuitive as plain lists. Unlike
46+other streaming libraries, no fancy types are required. Streamly is simply a
47+generalization of Haskell lists to monadic streaming optionally with concurrent
48+composition. The basic stream type in streamly `SerialT m a` can be considered
49+as a list type `[a]` parameterized by the monad `m`. For example, `SerialT IO
50+a` is a moral equivalent of `[a]` in the IO monad. `SerialT Identity a`, is
51+equivalent to pure lists. Streams are constructed very much like lists, except
52+that they use `nil` and `cons` instead of `[]` and `:`. Unlike lists, streams
53+can be constructed from monadic effects, not just pure elements. Streams are
54+processed just like lists, with list like combinators, except that they are
55+monadic and work in a streaming fashion. In other words streamly just completes
56+what lists lack, you do not need to learn anything new. Please see [streamly vs
57+lists](docs/streamly-vs-lists.md) for a detailed comparison.
58+
59+Not surprisingly, the monad instance of streamly is a list transformer, with
60+concurrency capability.
61+
62+## Why data flow programming?
63+
64+If you need some convincing for using streaming or data flow programming
65+paradigm itself then try to answer this question - why do we use lists in
66+Haskell? It boils down to why we use functional programming in the first place.
67+Haskell is successful in enforcing the functional data flow paradigm for pure
68+computations using lists, but not for monadic computations. In the absence of a
69+standard and easy to use data flow programming paradigm for monadic
70+computations, and the IO monad providing an escape hatch to an imperative
71+model, we just love to fall into the imperative trap, and start asking the same
72+fundamental question again - why do we have to use the streaming data model?
73+
74+## Comparative Performance
75+
76+High performance and simplicity are the two primary goals of streamly.
77+`Streamly` employs two different stream representations (CPS and direct style)
78+and interconverts between the two to get the best of both worlds on different
79+operations. It uses both foldr/build (for CPS style) and stream fusion (for
80+direct style) techniques to fuse operations. In terms of performance,
81+Streamly's goal is to compete with equivalent C programs. Streamly redefines
82+"blazing fast" for streaming libraries, it competes with lists and `vector`.
83+Other streaming libraries like "streaming", "pipes" and "conduit" are orders of
84+magnitude slower on most microbenchmarks. See [streaming
85+benchmarks](https://github.com/composewell/streaming-benchmarks) for detailed
86+comparison.
87+
88+The following chart shows a comparison of those streamly and list operations
89+where performance of the two differs by more than 10%. Positive y-axis displays
90+how many times worse is a list operation compared to the same streamly
91+operation, negative y-axis shows where streamly is worse compared to lists.
92+
93+![Streamly vs Lists (time) comparison](charts-0/streamly-vs-list-time.svg)
94+
95+Streamly uses lock-free synchronization for concurrent operations. It employs
96+auto-scaling of the degree of concurrency based on demand. For CPU bound tasks
97+it tries to keep the threads close to the number of CPUs available whereas for
98+IO bound tasks more threads can be utilized. Parallelism can be utilized with
99+little overhead even if the task size is very small. See [concurrency
100+benchmarks](https://github.com/composewell/concurrency-benchmarks) for detailed
101+performance results and a comparison with the `async` package.
102+
103+## Installing and using
104+
105+Please see [INSTALL.md](./INSTALL.md) for instructions on how to use streamly
106+with your Haskell build tool or package manager. You may want to go through it
107+before jumping to run the examples below.
108+
109+The module `Streamly.Prelude` provides the core stream types and combinators
110+for type casting, controlling concurrency, stream construction, transformation,
111+folding, merging, and zipping.
112+
113+## Streaming Pipelines
114+
115+The following snippet provides a simple stream composition example that reads
116+numbers from stdin, prints the squares of even numbers and exits if an even
117+number more than 9 is entered.
118+
119+``` haskell
120+import qualified Streamly.Prelude as S
121+import Data.Function ((&))
122+
123+main = S.drain $
124+ S.repeatM getLine
125+ & fmap read
126+ & S.filter even
127+ & S.takeWhile (<= 9)
128+ & fmap (\x -> x * x)
129+ & S.mapM print
130+```
131+
132+Unlike `pipes` or `conduit` and like `vector` and `streaming`, `streamly`
133+composes stream data instead of stream processors (functions). A stream is
134+just like a list and is explicitly passed around to functions that process the
135+stream. Therefore, no special operator is needed to join stages in a streaming
136+pipeline, just the standard function application (`$`) or reverse function
137+application (`&`) operator is enough.
138+
139+## Concurrent Stream Generation
140+
141+`consM` or its operator form `|:` can be used to construct a stream from
142+monadic actions. A stream constructed with `consM` can run the monadic actions
143+in the stream concurrently when used with appropriate stream type combinator
144+(e.g. `fromAsync`, `fromAhead` or `fromParallel`).
145+
146+The following code finishes in 3 seconds (6 seconds when serial), note the
147+order of elements in the resulting output, the outputs are consumed as soon as
148+each action is finished (asyncly):
149+
150+``` haskell
151+> let p n = threadDelay (n * 1000000) >> return n
152+> S.toList $ S.fromAsync $ p 3 |: p 2 |: p 1 |: S.nil
153+[1,2,3]
154+```
155+
156+Use `fromAhead` if you want speculative concurrency i.e. execute the actions in
157+the stream concurrently but consume the results in the specified order:
158+
159+``` haskell
160+> S.toList $ S.fromAhead $ p 3 |: p 2 |: p 1 |: S.nil
161+[3,2,1]
162+```
163+
164+Monadic stream generation functions e.g. `unfoldrM`, `replicateM`, `repeatM`,
165+`iterateM` and `fromFoldableM` etc. can work concurrently.
166+
167+The following finishes in 10 seconds (100 seconds when serial):
168+
169+``` haskell
170+S.drain $ S.fromAsync $ S.replicateM 10 $ p 10
171+```
172+
173+## Concurrency Auto Scaling
174+
175+Concurrency is auto-scaled i.e. more actions are executed concurrently if the
176+consumer is consuming the stream at a higher speed. How many tasks are executed
177+concurrently can be controlled by `maxThreads` and how many results are
178+buffered ahead of consumption can be controlled by `maxBuffer`. See the
179+documentation in the `Streamly.Prelude` module.
180+
181+## Concurrent Streaming Pipelines
182+
183+Use `|&` or `|$` to apply stream processing functions concurrently. The
184+following example prints a "hello" every second; if you use `&` instead of
185+`|&` you will see that the delay doubles to 2 seconds instead because of serial
186+application.
187+
188+``` haskell
189+main = S.drain $
190+ S.repeatM (threadDelay 1000000 >> return "hello")
191+ |& S.mapM (\x -> threadDelay 1000000 >> putStrLn x)
192+```
193+
194+## Mapping Concurrently
195+
196+We can use `mapM` or `sequence` functions concurrently on a stream.
197+
198+``` haskell
199+> let p n = threadDelay (n * 1000000) >> return n
200+> S.drain $ S.fromAhead $ S.mapM (\x -> p 1 >> print x) (S.fromSerial $ S.repeatM (p 1))
201+```
202+
203+## Serial and Concurrent Merging
204+
205+Semigroup and Monoid instances can be used to fold streams serially or
206+concurrently. In the following example we compose ten actions in the
207+stream, each with a delay of 1 to 10 seconds, respectively. Since all the
208+actions are concurrent we see one output printed every second:
209+
210+``` haskell
211+import qualified Streamly.Prelude as S
212+import Control.Concurrent (threadDelay)
213+
214+main = S.toList $ S.fromParallel $ foldMap delay [1..10]
215+ where delay n = S.yieldM $ threadDelay (n * 1000000) >> print n
216+```
217+
218+Streams can be combined together in many ways. We provide some examples
219+below, see the tutorial for more ways. We use the following `delay`
220+function in the examples to demonstrate the concurrency aspects:
221+
222+``` haskell
223+import qualified Streamly.Prelude as S
224+import Control.Concurrent
225+
226+delay n = S.yieldM $ do
227+ threadDelay (n * 1000000)
228+ tid <- myThreadId
229+ putStrLn (show tid ++ ": Delay " ++ show n)
230+```
231+### Serial
232+
233+``` haskell
234+main = S.drain $ delay 3 <> delay 2 <> delay 1
235+```
236+```
237+ThreadId 36: Delay 3
238+ThreadId 36: Delay 2
239+ThreadId 36: Delay 1
240+```
241+
242+### Parallel
243+
244+``` haskell
245+main = S.drain . S.fromParallel $ delay 3 <> delay 2 <> delay 1
246+```
247+```
248+ThreadId 42: Delay 1
249+ThreadId 41: Delay 2
250+ThreadId 40: Delay 3
251+```
252+
253+## Nested Loops (aka List Transformer)
254+
255+The monad instance composes like a list monad.
256+
257+``` haskell
258+import qualified Streamly.Prelude as S
259+
260+loops = do
261+ x <- S.fromFoldable [1,2]
262+ y <- S.fromFoldable [3,4]
263+ S.yieldM $ putStrLn $ show (x, y)
264+
265+main = S.drain loops
266+```
267+```
268+(1,3)
269+(1,4)
270+(2,3)
271+(2,4)
272+```
273+
274+## Concurrent Nested Loops
275+
276+To run the above code with speculative concurrency i.e. each iteration in the
277+loop can run concurrently but the results are presented to the consumer of the
278+output in the same order as serial execution:
279+
280+``` haskell
281+main = S.drain $ S.fromAhead $ loops
282+```
283+
284+Different stream types execute the loop iterations in different ways. For
285+example, `fromWSerial` interleaves the loop iterations. There are several
286+concurrent stream styles to execute the loop iterations concurrently in
287+different ways, see the `Streamly.Tutorial` module for a detailed treatment.
288+
289+## Magical Concurrency
290+
291+Streams can perform semigroup (<>) and monadic bind (>>=) operations
292+concurrently using combinators like `fromAsync`, `parallelly`. For example,
293+to concurrently generate squares of a stream of numbers and then concurrently
294+sum the square roots of all combinations of two streams:
295+
296+``` haskell
297+import qualified Streamly.Prelude as S
298+
299+main = do
300+ s <- S.sum $ S.fromAsync $ do
301+ -- Each square is performed concurrently, (<>) is concurrent
302+ x2 <- foldMap (\x -> return $ x * x) [1..100]
303+ y2 <- foldMap (\y -> return $ y * y) [1..100]
304+ -- Each addition is performed concurrently, monadic bind is concurrent
305+ return $ sqrt (x2 + y2)
306+ print s
307+```
308+
309+The concurrency facilities provided by streamly can be compared with
310+[OpenMP](https://en.wikipedia.org/wiki/OpenMP) and
311+[Cilk](https://en.wikipedia.org/wiki/Cilk) but with a more declarative
312+expression.
313+
314+## Example: Listing Directories Recursively/Concurrently
315+
316+The following code snippet lists a directory tree recursively, reading multiple
317+directories concurrently:
318+
319+```haskell
320+import Control.Monad.IO.Class (liftIO)
321+import Path.IO (listDir, getCurrentDir) -- from path-io package
322+import Streamly.Prelude (AsyncT, adapt)
323+import qualified Streamly.Prelude as S
324+
325+listDirRecursive :: AsyncT IO ()
326+listDirRecursive = getCurrentDir >>= readdir >>= liftIO . mapM_ putStrLn
327+ where
328+ readdir dir = do
329+ (dirs, files) <- listDir dir
330+ S.yield (map show dirs ++ map show files) <> foldMap readdir dirs
331+
332+main :: IO ()
333+main = S.drain $ adapt $ listDirRecursive
334+```
335+
336+`AsyncT` is a stream monad transformer. If you are familiar with a list
337+transformer, it is nothing but `ListT` with concurrency semantics. For example,
338+the semigroup operation `<>` is concurrent. This makes `foldMap` concurrent
339+too. You can replace `AsyncT` with `SerialT` and the above code will become
340+serial, exactly equivalent to a `ListT`.
341+
342+## Rate Limiting
343+
344+For bounded concurrent streams, stream yield rate can be specified. For
345+example, to print hello once every second you can simply write this:
346+
347+``` haskell
348+import Streamly.Prelude as S
349+
350+main = S.drain $ S.fromAsync $ S.avgRate 1 $ S.repeatM $ putStrLn "hello"
351+```
352+
353+For some practical uses of rate control, see
354+[AcidRain.hs](https://github.com/composewell/streamly/tree/master/examples/AcidRain.hs)
355+and
356+[CirclingSquare.hs](https://github.com/composewell/streamly/tree/master/examples/CirclingSquare.hs)
357+.
358+Concurrency of the stream is automatically controlled to match the specified
359+rate. Rate control works precisely even at throughputs as high as millions of
360+yields per second. For more sophisticated rate control see the haddock
361+documentation.
362+
363+## Arrays
364+
365+The `Streamly.Data.Array.Foreign` module provides immutable arrays. Arrays are the
366+computing duals of streams. Streams are good at sequential access and immutable
367+transformations of in-transit data whereas arrays are good at random access and
368+in-place transformations of buffered data. Unlike streams which are potentially
369+infinite, arrays are necessarily finite. Arrays can be used as an efficient
370+interface between streams and external storage systems like memory, files and
371+network. Streams and arrays complete each other to provide a general purpose
372+computing system. The design of streamly as a general purpose computing
373+framework is centered around these two fundamental aspects of computing and
374+storage.
375+
376+`Streamly.Data.Array.Foreign` uses pinned memory outside GC and therefore avoid any
377+GC overhead for the storage in arrays. Streamly allows efficient
378+transformations over arrays using streams. It uses arrays to transfer data to
379+and from the operating system and to store data in memory.
380+
381+## Folds
382+
383+Folds are consumers of streams. `Streamly.Data.Fold` module provides a `Fold`
384+type that represents a `foldl'`. Such folds can be efficiently composed
385+allowing the compiler to perform stream fusion and therefore implement high
386+performance combinators for consuming streams. A stream can be distributed to
387+multiple folds, or it can be partitioned across multiple folds, or
388+demultiplexed over multiple folds, or unzipped to two folds. We can also use
389+folds to fold segments of stream generating a stream of the folded results.
390+
391+If you are familiar with the `foldl` library, these are the same composable
392+left folds but simpler and better integrated with streamly, and with many more
393+powerful ways of composing and applying them.
394+
395+## Unfolds
396+
397+Unfolds are duals of folds. Folds help us compose consumers of streams
398+efficiently and unfolds help us compose producers of streams efficiently.
399+`Streamly.Data.Unfold` provides an `Unfold` type that represents an `unfoldr`
400+or a stream generator. Such generators can be combined together efficiently
401+allowing the compiler to perform stream fusion and implement high performance
402+stream merging combinators.
403+
404+## File IO
405+
406+The following code snippets implement some common Unix command line utilities
407+using streamly. You can compile these with `ghc -O2 -fspec-constr-recursive=16
408+-fmax-worker-args=16` and compare the performance with regular GNU coreutils
409+available on your system. Though many of these are not most optimal solutions
410+to keep them short and elegant. Source file
411+[HandleIO.hs](https://github.com/composewell/streamly/tree/master/examples/HandleIO.hs)
412+in the examples directory includes these examples.
413+
414+``` haskell
415+module Main where
416+
417+import qualified Streamly.Prelude as S
418+import qualified Streamly.Data.Fold as FL
419+import qualified Streamly.Data.Array.Foreign as A
420+import qualified Streamly.FileSystem.Handle as FH
421+import qualified System.IO as FH
422+
423+import Data.Char (ord)
424+import System.Environment (getArgs)
425+import System.IO (openFile, IOMode(..), stdout)
426+
427+withArg f = do
428+ (name : _) <- getArgs
429+ src <- openFile name ReadMode
430+ f src
431+
432+withArg2 f = do
433+ (sname : dname : _) <- getArgs
434+ src <- openFile sname ReadMode
435+ dst <- openFile dname WriteMode
436+ f src dst
437+```
438+
439+### cat
440+
441+``` haskell
442+cat = S.fold (FH.writeChunks stdout) . S.unfold FH.readChunks
443+main = withArg cat
444+```
445+
446+### cp
447+
448+``` haskell
449+cp src dst = S.fold (FH.writeChunks dst) $ S.unfold FH.readChunks src
450+main = withArg2 cp
451+```
452+
453+### wc -l
454+
455+``` haskell
456+wcl = S.length . S.splitOn (== 10) FL.drain . S.unfold FH.read
457+main = withArg wcl >>= print
458+```
459+
460+### Average Line Length
461+
462+``` haskell
463+avgll =
464+ S.fold avg
465+ . S.splitOn (== 10) FL.length
466+ . S.unfold FH.read
467+
468+ where avg = (/) <$> toDouble FL.sum <*> toDouble FL.length
469+ toDouble = fmap (fromIntegral :: Int -> Double)
470+
471+main = withArg avgll >>= print
472+```
473+
474+### Line Length Histogram
475+
476+`classify` is not released yet, and is available in
477+`Streamly.Internal.Data.Fold`
478+
479+``` haskell
480+llhisto =
481+ S.fold (FL.classify FL.length)
482+ . S.map bucket
483+ . S.splitOn (== 10) FL.length
484+ . S.unfold FH.read
485+
486+ where
487+ bucket n = let i = n `mod` 10 in if i > 9 then (9,n) else (i,n)
488+
489+main = withArg llhisto >>= print
490+```
491+
492+## Socket IO
493+
494+Its easy to build concurrent client and server programs using streamly.
495+`Streamly.Network.*` modules provide easy combinators to build network servers
496+and client programs using streamly. See
497+[FromFileClient.hs](https://github.com/composewell/streamly/tree/master/examples/FromFileClient.hs),
498+[EchoServer.hs](https://github.com/composewell/streamly/tree/master/examples/EchoServer.hs),
499+[FileSinkServer.hs](https://github.com/composewell/streamly/tree/master/examples/FileSinkServer.hs)
500+in the examples directory.
501+
502+## Exceptions
503+
504+Exceptions can be thrown at any point using the `MonadThrow` instance. Standard
505+exception handling combinators like `bracket`, `finally`, `handle`,
506+`onException` are provided in `Streamly.Prelude` module.
507+
508+In presence of concurrency, synchronous exceptions work just the way they are
509+supposed to work in non-concurrent code. When concurrent streams
510+are combined together, exceptions from the constituent streams are propagated
511+to the consumer stream. When an exception occurs in any of the constituent
512+streams other concurrent streams are promptly terminated.
513+
514+There is no notion of explicit threads in streamly, therefore, no
515+asynchronous exceptions to deal with. You can just ignore the zillions of
516+blogs, talks, caveats about async exceptions. Async exceptions just don't
517+exist. Please don't use things like `myThreadId` and `throwTo` just for fun!
518+
519+## Reactive Programming (FRP)
520+
521+Streamly is a foundation for first class reactive programming as well by virtue
522+of integrating concurrency and streaming. See
523+[AcidRain.hs](https://github.com/composewell/streamly/tree/master/examples/AcidRain.hs)
524+for a console based FRP game example and
525+[CirclingSquare.hs](https://github.com/composewell/streamly/tree/master/examples/CirclingSquare.hs)
526+for an SDL based animation example.
527+
528+## Conclusion
529+
530+Streamly, short for streaming concurrently, provides monadic streams, with a
531+simple API, almost identical to standard lists, and an in-built
532+support for concurrency. By using stream-style combinators on stream
533+composition, streams can be generated, merged, chained, mapped, zipped, and
534+consumed concurrently – providing a generalized high level programming
535+framework unifying streaming and concurrency. Controlled concurrency allows
536+even infinite streams to be evaluated concurrently. Concurrency is auto scaled
537+based on feedback from the stream consumer. The programmer does not have to be
538+aware of threads, locking or synchronization to write scalable concurrent
539+programs.
540+
541+Streamly is a programmer first library, designed to be useful and friendly to
542+programmers for solving practical problems in a simple and concise manner. Some
543+key points in favor of streamly are:
544+
545+ * _Simplicity_: Simple list like streaming API, if you know how to use lists
546+ then you know how to use streamly. This library is built with simplicity
547+ and ease of use as a design goal.
548+ * _Concurrency_: Simple, powerful, and scalable concurrency. Concurrency is
549+ built-in, and not intrusive, concurrent programs are written exactly the
550+ same way as non-concurrent ones.
551+ * _Generality_: Unifies functionality provided by several disparate packages
552+ (streaming, concurrency, list transformer, logic programming, reactive
553+ programming) in a concise API.
554+ * _Performance_: Streamly is designed for high performance. It employs stream
555+ fusion optimizations for best possible performance. Serial peformance is
556+ equivalent to the venerable `vector` library in most cases and even better
557+ in some cases. Concurrent performance is unbeatable. See
558+ [streaming-benchmarks](https://github.com/composewell/streaming-benchmarks)
559+ for a comparison of popular streaming libraries on micro-benchmarks.
560+
561+The basic streaming functionality of streamly is equivalent to that provided by
562+streaming libraries like
563+[vector](https://hackage.haskell.org/package/vector),
564+[streaming](https://hackage.haskell.org/package/streaming),
565+[pipes](https://hackage.haskell.org/package/pipes), and
566+[conduit](https://hackage.haskell.org/package/conduit).
567+In addition to providing streaming functionality, streamly subsumes
568+the functionality of list transformer libraries like `pipes` or
569+[list-t](https://hackage.haskell.org/package/list-t), and also the logic
570+programming library [logict](https://hackage.haskell.org/package/logict). On
571+the concurrency side, it subsumes the functionality of the
572+[async](https://hackage.haskell.org/package/async) package, and provides even
573+higher level concurrent composition. Because it supports
574+streaming with concurrency we can write FRP applications similar in concept to
575+[Yampa](https://hackage.haskell.org/package/Yampa) or
576+[reflex](https://hackage.haskell.org/package/reflex).
577+
578+See the `Comparison with existing packages` section at the end of the
579+[tutorial](https://hackage.haskell.org/package/streamly/docs/Streamly-Tutorial.html).
580+
581+## Support
582+
583+Please feel free to ask questions on the
584+[streamly gitter channel](https://gitter.im/composewell/streamly).
585+If you require professional support, consulting, training or timely
586+enhancements to the library please contact
587+[support@composewell.com](mailto:support@composewell.com).
588+
589+## Credits
590+
591+The following authors/libraries have influenced or inspired this library in a
592+significant way:
593+
594+ * Roman Leshchinskiy ([vector](http://hackage.haskell.org/package/vector))
595+ * Gabriel Gonzalez ([foldl](https://hackage.haskell.org/package/foldl))
596+ * Alberto G. Corona ([transient](https://hackage.haskell.org/package/transient))
597+
598+See the `credits` directory for full list of contributors, credits and licenses.
599+
600+## Contributing
601+
602+The code is available under BSD-3 license
603+[on github](https://github.com/composewell/streamly). Join the [gitter
604+chat](https://gitter.im/composewell/streamly) channel for discussions. Please
605+ask any questions on the gitter channel or [contact the maintainer
606+directly](mailto:streamly@composewell.com). All contributions are welcome!
streamly.cabal+1
@@ -112,6 +112,7 @@ extra-source-files:
112112 design/*.md
113113 design/*.png
114114 design/*.rst
115+ docs/Overview.md
115116 docs/Build.md
116117 docs/streamly-vs-async.md
117118 docs/streamly-vs-lists.md