@@ -1,16 +1,79 @@
1−import Control.Applicative ((<|>))
1+import Control.Applicative ((<|>), empty)
2−import Control.Concurrent (threadDelay)
2+import Control.Concurrent (threadDelay, myThreadId)
3import Control.Monad.IO.Class (liftIO)
3import Control.Monad.IO.Class (liftIO)
4+import Data.Monoid ((<>))
4import System.Random (randomIO)
5import System.Random (randomIO)
6+import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
5import Asyncly
7import Asyncly
7−main = wait_ $ do
9+main = do
8− x <- loop
10+ liftIO $ hSetBuffering stdout LineBuffering
13+ x <- loopTail (0 :: Int)
17+ x <- loopHead (0 :: Int)
21+ x <- loopTailA (0 :: Int)
25+ x <- loopHeadA (0 :: Int)
9− liftIO $ print x
26+ liftIO $ print x
13− loop = do
30+-------------------------------------------------------------------------------
14− liftIO $ threadDelay 1000000
31+-- Serial (single-threaded) stream generator loops
15− x <- liftIO (randomIO :: IO Int)
32+-------------------------------------------------------------------------------
34+ -- In a <> composition the action on the left is executed and only after it
35+ -- finished then the action on the right is executed. In other words the
36+ -- actions are run serially.
38+ -- Generates a value and then loops. Can be used to generate an infinite
39+ -- stream. Interleaves the generator and the consumer.
40+ loopTail :: Int -> AsynclyT IO Int
42+ liftIO $ putStrLn "LoopTail..."
43+ return x <> (if x < 3 then loopTail (x + 1) else empty)
45+ -- Loops and then generates a value. The consumer can run only after the
46+ -- loop has finished. An infinite generator will not let the consumer run
48+ loopHead :: Int -> AsynclyT IO Int
50+ liftIO $ putStrLn "LoopHead..."
51+ (if x < 3 then loopHead (x + 1) else empty) <> return x
53+-------------------------------------------------------------------------------
54+-- Concurrent (multi-threaded) adaptive demand-based stream generator loops
55+-------------------------------------------------------------------------------
57+ -- In a <|> composition the action on the left is executed first. However,
58+ -- if it is not fast enough to generate results at the consumer's speed
59+ -- then the action on the right is also spawned concurrently. In other
60+ -- words, both actions may run concurrently based on the need.
62+ loopTailA :: Int -> AsynclyT IO Int
64+ liftIO $ putStrLn "LoopTailA..."
65+ return x <|> (if x < 3 then loopTailA (x + 1) else empty)
67+ loopHeadA :: Int -> AsynclyT IO Int
69+ liftIO $ putStrLn "LoopHeadA..."
70+ (if x < 3 then loopHeadA (x + 1) else empty) <|> return x
72+-------------------------------------------------------------------------------
73+-- Parallel (fairly scheduled, multi-threaded) stream generator loops
74+-------------------------------------------------------------------------------
76+ -- In a <||> composition both actions are run concurrently in a fair
77+ -- manner, no one action is preferred over another. Both actions are
78+ -- spawned right away in their own independent threads. In other words, the
79+ -- actions will run concurrently.