@@ -0,0 +1,229 @@
1+-- |
2+-- Module : Asyncly.Tutorial
3+-- Copyright : (c) 2017 Harendra Kumar
4+--
5+-- License : BSD3
6+-- Maintainer : harendra.kumar@gmail.com
7+--
8+-- 'AsyncT' is like a list transformer ('ListT' or 'LogicT') that supports
9+-- concurrent composition. Lists or streams can be generated using serial or
10+-- concurrent monadic actions. A stream could be a stream of data or a stream
11+-- of asynchronous events in an event driven program. Streams can be appended
12+-- serially (<>) or interleaved fairly (\<=>), concurrent streams can be
13+-- unioned like sets (\<|) or interleaved fairly (\<|>). Streams can be
14+-- multiplied as in a list monad using the bind (>>=) operation.
15+--
16+-- Here is an example program that repeatedly reads strings from standard input
17+-- and echos them to the standard output:
18+--
19+-- @
20+-- import Asyncly
21+-- import Data.Semigroup (cycle1)
22+--
23+-- main = runAsyncly $ cycle1 (liftIO getLine) >>= liftIO . putStrLn
24+-- @
25+--
26+-- You can fold with (\<|) or (\<|>) to run actions concurrently. For
27+-- example to find the fastest search engine:
28+--
29+-- @
30+-- import Asyncly
31+-- import Network.HTTP.Simple
32+--
33+-- main = runAsyncly $ google \<|> bing \<|> duckduckgo
34+-- where
35+-- google = get "https://www.google.com/search?q=haskell"
36+-- bing = get "https://www.bing.com/search?q=haskell"
37+-- duckduckgo = get "https://www.duckduckgo.com/?q=haskell"
38+-- get s = liftIO (httpNoBody (parseRequest_ s) >> putStrLn (show s))
39+-- @
40+--
41+-- The above examples just used applicative or alternative compositions. They
42+-- can be done equivalently using async package as well. However the real power
43+-- of asyncly is in the monadic composition. Monadic composition allows
44+-- creating state machines or event networks or multiplying streams whatever
45+-- way you want to think about it. It is a very powerful general, concurrent
46+-- and reactive programming tool.
47+--
48+-- Let us now see an applicative example of the same:
49+--
50+-- Let's take an example of listing a directory tree:
51+--
52+-- @
53+-- import Path.IO (listDir, getCurrentDir)
54+-- import Asyncly
55+--
56+-- main = runAsyncly $ getCurrentDir >>= readdir
57+-- where readdir d = do
58+-- (dirs, files) <- lift $ listDir d
59+-- liftIO $ mapM_ putStrLn $ map show files
60+-- foldr (<>) mempty $ map readdir dirs
61+-- @
62+--
63+-- Just think that you had applicative and no monad in Haskell. We have
64+-- streaming libraries that are equivalent to applicative and asyncly is the
65+-- equivalent of the monadic composition of streams. We have had that in th
66+-- form of half hearted list transformers but asyncly provides a full fledged
67+-- concurent list transformer.
68+--
69+-- Let us see a reactive programming example:
70+--
71+-- @
72+-- import Asyncly
73+-- import Control.Concurrent (threadDelay)
74+-- import Control.Monad (when)
75+-- import Control.Monad.State
76+-- import Data.Semigroup (cycle1)
77+--
78+-- data Event = Harm Int | Heal Int | Quit deriving (Show)
79+--
80+-- userAction :: MonadIO m => AsyncT m Event
81+-- userAction = cycle1 $ liftIO askUser
82+-- where
83+-- askUser = do
84+-- command <- getLine
85+-- case command of
86+-- "potion" -> return (Heal 10)
87+-- "quit" -> return Quit
88+-- _ -> putStrLn "What?" >> askUser
89+--
90+-- acidRain :: MonadIO m => AsyncT m Event
91+-- acidRain = cycle1 $ liftIO (threadDelay 1000000) >> return (Harm 1)
92+--
93+-- game :: (MonadAsync m, MonadState Int m) => AsyncT m ()
94+-- game = do
95+-- event <- userAction <|> acidRain
96+-- case event of
97+-- Harm n -> modify $ \h -> h - n
98+-- Heal n -> modify $ \h -> h + n
99+-- Quit -> fail "quit"
100+--
101+-- h <- get
102+-- when (h <= 0) $ fail "You die!"
103+-- liftIO $ putStrLn $ "Health = " ++ show h
104+--
105+-- main = do
106+-- putStrLn "Your health is deteriorating due to acid rain,\
107+-- \ type \"potion\" or \"quit\""
108+-- runStateT (runAsyncly game) 60
109+-- @
110+--
111+-- You can create trees of demand driven parallel computations. Parallel
112+-- computations are started faster if the consumer is pulling faster. The
113+-- number of threads are automatically adjusted and are kept at a minimum
114+-- required depending on the consumer rate.
115+
116+-- Asyncly allows expressing and composing state machines and event driven
117+-- programs in a straightforward manner. It allows waiting for events to occur
118+-- in a non-blocking manner and process them asynchronously; multiple events
119+-- can be processed in parallel. Tasks can be easily split into smaller tasks,
120+-- processed in parallel and results combined.
121+--
122+-- The 'Alternative' composition @(\<|\>)@ is used to express asynchronous or
123+-- parallel tasks. The following example demonstrates generation and printing
124+-- of random numbers happening in parallel:
125+--
126+-- @
127+-- import Control.Applicative ((\<|\>))
128+-- import Control.Concurrent (threadDelay)
129+-- import Control.Monad.IO.Class (liftIO)
130+-- import System.Random (randomIO)
131+-- import Asyncly
132+--
133+-- main = wait_ $ do
134+-- x <- loop
135+-- liftIO $ print x
136+--
137+-- where
138+--
139+-- loop = do
140+-- liftIO $ threadDelay 1000000
141+-- x <- liftIO (randomIO :: IO Int)
142+-- return x \<|\> loop
143+-- @
144+--
145+-- Here two random number generation loops run in parallel so two numbers are
146+-- printed every second. Note that the threadId printed for each is different:
147+--
148+-- @
149+-- import Control.Applicative ((\<|\>))
150+-- import Control.Concurrent (myThreadId, threadDelay)
151+-- import Control.Monad.IO.Class (liftIO)
152+-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
153+-- import System.Random (randomIO)
154+-- import Asyncly
155+--
156+-- main = wait_ $ do
157+-- liftIO $ hSetBuffering stdout LineBuffering
158+-- x <- loop \"A" \<|\> loop \"B"
159+-- liftIO $ myThreadId >>= putStr . show
160+-- >> putStr " "
161+-- >> print x
162+--
163+-- where
164+--
165+-- loop name = do
166+-- liftIO $ threadDelay 1000000
167+-- rnd <- liftIO (randomIO :: IO Int)
168+-- return (name, rnd) \<|\> loop name
169+-- @
170+-- Here the two loops are serially composed. For each value yielded by loop A,
171+-- loop B is executed. Four results are printed, all four run in separate
172+-- parallel threads. The composition is like ListT except that this is
173+-- concurrent:
174+--
175+-- @
176+-- import Control.Applicative ((\<|\>), empty)
177+-- import Control.Concurrent (myThreadId)
178+-- import Control.Monad.IO.Class (liftIO)
179+-- import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
180+-- import System.Random (randomIO)
181+-- import Asyncly
182+--
183+-- main = wait_ $ do
184+-- liftIO $ hSetBuffering stdout LineBuffering
185+-- x <- loop "A " 2
186+-- y <- loop "B " 2
187+-- liftIO $ myThreadId >>= putStr . show
188+-- >> putStr " "
189+-- >> print (x, y)
190+--
191+-- where
192+--
193+-- loop name n = do
194+-- rnd <- liftIO (randomIO :: IO Int)
195+-- let result = (name ++ show rnd)
196+-- repeat = if n > 1 then loop name (n - 1) else empty
197+-- in (return result) \<|\> repeat
198+-- @
199+--
200+-- Here we perform a simple multi-threaded map-reduce by squaring each number
201+-- in a separate thread and then summing the squares:
202+--
203+-- @
204+-- import Control.Applicative ((\<|\>), empty)
205+-- import Data.List (sum)
206+-- import Asyncly
207+--
208+-- main = do
209+-- squares <- wait $ do
210+-- x <- foldl (\<|\>) empty $ map return [1..100]
211+-- return (x * x)
212+-- print . sum $ squares
213+-- @
214+--
215+-- 'Applicative' and 'Monoid' compositions work as expected.
216+
217+-- It can be thought of as a non-deterministic continuation monad or a
218+-- combination of ContT and ListT. It allows to capture the state of the
219+-- application at any point and trigger arbitrary number of continuations from
220+-- the capture point. Non-determinism or parallel continuations are introduced
221+-- using the <|> operator. It provides a convenient way to implement and
222+-- compose state machines without using callbacks. An event in the state
223+-- machine corresponds to a continuation. There are no cycles in the state
224+-- machine as each transition in the state machine is an independent instance
225+-- of the state machine. It is an immutable state machine!
226+
227+module Asyncly.Tutorial
228+ ()
229+where