on master help sign in
Move examples to streamly-examples repo.

Closes #684

commitd65f2b40457d808bea73d8cb310b60785d6105ca authorpranaysashank <pranaysashank@composewell.com> dateFri 18 Sep 2020, 16:37 (6 years ago) parentc54581e
20 files changed+25−1849
.travis.yml+6−6 examples/AcidRain.hs−61 examples/CamelCase.hs−33 examples/CirclingSquare.hs−82 examples/ControlFlow.hs−308 examples/EchoServer.hs−21 examples/FileIOExamples.hs−65 examples/FileSinkServer.hs−37 examples/FromFileClient.hs−20 examples/HandleIO.hs−107 examples/ListDir.hs−23 examples/MergeSort.hs−23 examples/README.md+2−43 examples/Rate.hs−10 examples/SearchQuery.hs−35 examples/Split.hs−38 examples/WordClassifier.hs−75 examples/WordCount.hs−628 src/Streamly/Tutorial.hs+16−14 streamly.cabal+1−220
.travis.yml+6−6
@@ -115,9 +115,9 @@ matrix:
115115 #- env: BUILD=cabal-v2 GHCVER=8.2.2 GHC_OPTIONS="" DISABLE_BENCH=y
116116 # addons: {apt: {packages: [cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
117117
118− - name: "GHC 8.0.2+examples-sdl"
119− env: BUILD=cabal-v2 GHCVER=8.0.2 GHC_OPTIONS="" CABAL_BUILD_OPTIONS="--flags examples-sdl"
120− addons: {apt: {packages: [cabal-install-3.2,ghc-8.0.2,libsdl1.2-dev], sources: [hvr-ghc]}}
118+ - name: "GHC 8.0.2"
119+ env: BUILD=cabal-v2 GHCVER=8.0.2 GHC_OPTIONS=""
120+ addons: {apt: {packages: [cabal-install-3.2,ghc-8.0.2], sources: [hvr-ghc]}}
121121 os: linux
122122 arch: amd64
123123
@@ -155,9 +155,9 @@ matrix:
155155 #- env: BUILD=stack RESOLVER=nightly
156156 # addons: {apt: {packages: [cabal-install-1.24], sources: [hvr-ghc]}}
157157
158− - name: "GHC 8.8+stack lts-16.12+examples-sdl"
159− env: BUILD=stack RESOLVER=lts-16.12 GHCVER=8.8 STACK_BUILD_OPTIONS="--flag streamly:examples-sdl" SDIST_OPTIONS="--ignore-check"
160− addons: {apt: {packages: [cabal-install-3.2,libsdl1.2-dev], sources: [hvr-ghc]}}
158+ - name: "GHC 8.8+stack lts-16.12"
159+ env: BUILD=stack RESOLVER=lts-16.12 GHCVER=8.8 SDIST_OPTIONS="--ignore-check"
160+ addons: {apt: {packages: [cabal-install-3.2], sources: [hvr-ghc]}}
161161 os: linux
162162 arch: amd64
163163
examples/AcidRain.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−
4−-- Copyright : (c) 2017 Composewell Technologies
5−-- (c) 2013, 2014 Gabriel Gonzalez
6−--
7−-- This example is adapted from Gabriel Gonzalez's pipes-concurrency package.
8−-- https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html
9−
10−#if !(MIN_VERSION_base(4,11,0))
11−import Data.Semigroup ((<>))
12−#endif
13−import Streamly.Prelude as S
14−import Control.Monad (void)
15−import Control.Monad.IO.Class (MonadIO(liftIO))
16−import Control.Monad.State (MonadState, get, modify, runStateT)
17−
18−data Event = Quit | Harm Int | Heal Int deriving (Show)
19−
20−userAction :: MonadAsync m => SerialT m Event
21−userAction = S.repeatM $ liftIO askUser
22− where
23− askUser = do
24− command <- getLine
25− case command of
26− "potion" -> return (Heal 10)
27− "harm" -> return (Harm 10)
28− "quit" -> return Quit
29− _ -> putStrLn "Type potion or harm or quit" >> askUser
30−
31−acidRain :: MonadAsync m => SerialT m Event
32−acidRain = S.asyncly $ S.constRate 1 $ S.repeatM $ liftIO $ return $ Harm 1
33−
34−data Result = Check | Done
35−
36−runEvents :: (MonadAsync m, MonadState Int m) => SerialT m Result
37−runEvents = do
38− event <- userAction `S.parallel` acidRain
39− case event of
40− Harm n -> modify (\h -> h - n) >> return Check
41− Heal n -> modify (\h -> h + n) >> return Check
42− Quit -> return Done
43−
44−data Status = Alive | GameOver deriving Eq
45−
46−getStatus :: (MonadAsync m, MonadState Int m) => Result -> m Status
47−getStatus result =
48− case result of
49− Done -> liftIO $ putStrLn "You quit!" >> return GameOver
50− Check -> do
51− h <- get
52− liftIO $ if (h <= 0)
53− then putStrLn "You die!" >> return GameOver
54− else putStrLn ("Health = " <> show h) >> return Alive
55−
56−main :: IO ()
57−main = do
58− putStrLn "Your health is deteriorating due to acid rain,\\
59− \ type \"potion\" or \"quit\""
60− let runGame = S.drainWhile (== Alive) $ S.mapM getStatus runEvents
61− void $ runStateT runGame 60
examples/CamelCase.hsdeleted−33
@@ -1,33 +0,0 @@
1−-- ghc -O2 -fspec-constr-recursive=10 -fmax-worker-args=16
2−-- Convert the input file to camel case and write to stdout
3−
4−import Data.Maybe (fromJust, isJust)
5−import System.Environment (getArgs)
6−import System.IO (Handle, IOMode(..), openFile, stdout)
7−
8−import qualified Streamly.Prelude as S
9−import qualified Streamly.Internal.FileSystem.Handle as FH
10−
11−camelCase :: Handle -> Handle -> IO ()
12−camelCase src dst =
13− FH.fromBytes dst
14− $ S.map fromJust
15− $ S.filter isJust
16− $ S.map snd
17− $ S.scanl' step (True, Nothing)
18− $ FH.toBytes src
19−
20− where
21−
22− step (wasSpace, _) x =
23− if x == 0x0a || x >= 0x41 && x <= 0x5a
24− then (False, Just x)
25− else if x >= 0x61 && x <= 0x7a
26− then (False, Just $ if wasSpace then x - 32 else x)
27− else (True, Nothing)
28−
29−main :: IO ()
30−main = do
31− name <- fmap head getArgs
32− src <- openFile name ReadMode
33− camelCase src stdout
examples/CirclingSquare.hsdeleted−82
@@ -1,82 +0,0 @@
1−-- Adapted from the Yampa package.
2−-- Displays a square moving in a circle. To move the position drag it with the
3−-- mouse.
4−--
5−-- Requires the SDL package, assuming streamly has already been built, you can
6−-- compile it like this:
7−-- stack ghc --package SDL CirclingSquare.hs
8−
9−import Data.IORef
10−import Graphics.UI.SDL as SDL
11−import Streamly.Prelude as S
12−
13−------------------------------------------------------------------------------
14−-- SDL Graphics Init
15−------------------------------------------------------------------------------
16−
17−sdlInit :: IO ()
18−sdlInit = do
19− SDL.init [InitVideo]
20−
21− let width = 640
22− height = 480
23− _ <- SDL.setVideoMode width height 16 [SWSurface]
24− SDL.setCaption "Test" ""
25−
26−------------------------------------------------------------------------------
27−-- Display a box at a given coordinates
28−------------------------------------------------------------------------------
29−
30−display :: (Double, Double) -> IO ()
31−display (playerX, playerY) = do
32− screen <- getVideoSurface
33−
34− -- Paint screen green
35− let format = surfaceGetPixelFormat screen
36− bgColor <- mapRGB format 55 60 64
37− _ <- fillRect screen Nothing bgColor
38−
39− -- Paint small red square, at an angle 'angle' with respect to the center
40− foreC <- mapRGB format 212 108 73
41− let side = 20
42− x = round playerX
43− y = round playerY
44− _ <- fillRect screen (Just (Rect x y side side)) foreC
45−
46− -- Double buffering
47− SDL.flip screen
48−
49−------------------------------------------------------------------------------
50−-- Wait and update Controller Position if it changes
51−------------------------------------------------------------------------------
52−
53−updateController :: IORef (Double, Double) -> IO ()
54−updateController ref = do
55− e <- pollEvent
56− case e of
57− MouseMotion x y _ _ -> writeIORef ref (fromIntegral x, fromIntegral y)
58− _ -> return ()
59−
60−------------------------------------------------------------------------------
61−-- Periodically refresh the output display
62−------------------------------------------------------------------------------
63−
64−updateDisplay :: IORef (Double, Double) -> IO ()
65−updateDisplay cref = do
66− time <- SDL.getTicks
67− (x, y) <- readIORef cref
68− let t = fromIntegral time * speed / 1000
69− in display (x + cos t * radius, y + sin t * radius)
70−
71− where
72−
73− speed = 6
74− radius = 60
75−
76−main :: IO ()
77−main = do
78− sdlInit
79− cref <- newIORef (0,0)
80− S.drain $ S.asyncly $ S.constRate 40
81− $ S.repeatM (updateController cref)
82− `S.parallel` S.repeatM (updateDisplay cref)
examples/ControlFlow.hsdeleted−308
@@ -1,308 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−
5−-------------------------------------------------------------------------------
6−-- Combining control flow manipulating monad transformers (MaybeT, exceptT,
7−-- ContT) with Streamly
8−-------------------------------------------------------------------------------
9−--
10−-- Streamly streams are non-determinism (nested looping) monads. We can use a
11−-- control flow monad on top or streamly on top depending on whether we want to
12−-- superimpose control flow manipulation on top of non-deterministic
13−-- composition or vice-versa.
14−--
15−-- This file provides an example where we enter a sequence of characters "x",
16−-- and "y" on separate lines, on the command line. When any other sequence is
17−-- entered the control flow short circuits at the first non-matching char and
18−
19−-- exits.
20−
21−#if !(MIN_VERSION_base(4,11,0))
22−import Data.Semigroup (Semigroup(..))
23−#endif
24−import Control.Concurrent (threadDelay)
25−import Control.Exception (catch, SomeException)
26−import Control.Monad
27−import Control.Monad.Catch (MonadThrow, throwM, Exception)
28−import Control.Monad.IO.Class
29−import Control.Monad.Trans.Class
30−import Control.Monad.Trans.Maybe
31−import Control.Monad.Trans.Except
32−import Control.Monad.Trans.Cont
33−import Streamly.Prelude
34−import qualified Streamly.Prelude as S
35−
36−-------------------------------------------------------------------------------
37−-- Using MaybeT below streamly
38−-------------------------------------------------------------------------------
39−--
40−-- When streamly is on top MaybeT would terminate all iterations of
41−-- non-determinism.
42−--
43−getSequenceMaybeBelow
44− :: ( IsStream t
45− , Monad m
46− , MonadTrans t
47− , MonadIO (t (MaybeT m))
48− )
49− => t (MaybeT m) ()
50−getSequenceMaybeBelow = do
51− liftIO $ putStrLn "MaybeT below streamly: Enter one char per line: "
52−
53− i <- S.fromFoldable [1..2 :: Int]
54− liftIO $ putStrLn $ "iteration = " <> show i
55−
56− r1 <- liftIO getLine
57− when (r1 /= "x") $ lift mzero
58−
59− r2 <- liftIO getLine
60− when (r2 /= "y") $ lift mzero
61−
62−mainMaybeBelow :: IO ()
63−mainMaybeBelow = do
64− r <- runMaybeT (S.drain getSequenceMaybeBelow)
65− case r of
66− Just _ -> putStrLn "Bingo"
67− Nothing -> putStrLn "Wrong"
68−
69−-------------------------------------------------------------------------------
70−-- Using MaybeT above streamly
71−-------------------------------------------------------------------------------
72−--
73−-- When MaybeT is on top a Nothing would terminate only the current iteration
74−-- of non-determinism below.
75−--
76−-- Note that this is redundant configuration as the same behavior can be
77−-- achieved with just streamly, using mzero.
78−--
79−getSequenceMaybeAbove :: (IsStream t, MonadIO (t m)) => MaybeT (t m) ()
80−getSequenceMaybeAbove = do
81− liftIO $ putStrLn "MaybeT above streamly: Enter one char per line: "
82−
83− i <- lift $ S.fromFoldable [1..2 :: Int]
84− liftIO $ putStrLn $ "iteration = " <> show i
85−
86− r1 <- liftIO getLine
87− when (r1 /= "x") mzero
88−
89− r2 <- liftIO getLine
90− when (r2 /= "y") mzero
91−
92−mainMaybeAbove :: (IsStream t, MonadIO (t m)) => MaybeT (t m) ()
93−mainMaybeAbove = do
94− getSequenceMaybeAbove
95− liftIO $ putStrLn "Bingo"
96−
97−-------------------------------------------------------------------------------
98−-- Using ExceptT below streamly
99−-------------------------------------------------------------------------------
100−--
101−-- XXX need to have a specialized liftCatch to lift catchE
102−--
103−-- Note that throwE would terminate all iterations of non-determinism
104−-- altogether.
105−getSequenceEitherBelow
106− :: ( IsStream t
107− , MonadTrans t
108− , Monad m
109− , MonadIO (t (ExceptT String m))
110− )
111− => t (ExceptT String m) ()
112−getSequenceEitherBelow = do
113− liftIO $ putStrLn "ExceptT below streamly: Enter one char per line: "
114−
115− i <- S.fromFoldable [1..2 :: Int]
116− liftIO $ putStrLn $ "iteration = " <> show i
117−
118− r1 <- liftIO getLine
119− when (r1 /= "x") $ lift $ throwE $ "Expecting x got: " <> r1
120−
121− r2 <- liftIO getLine
122− when (r2 /= "y") $ lift $ throwE $ "Expecting y got: " <> r2
123−
124−mainEitherBelow :: IO ()
125−mainEitherBelow = do
126− -- XXX Cannot lift catchE
127− r <- runExceptT (S.drain getSequenceEitherBelow)
128− case r of
129− Right _ -> liftIO $ putStrLn "Bingo"
130− Left s -> liftIO $ putStrLn s
131−
132−-------------------------------------------------------------------------------
133−-- Using ExceptT below concurrent streamly
134−-------------------------------------------------------------------------------
135−--
136−-- XXX does not work correctly yet
137−--
138−getSequenceEitherAsyncBelow
139− :: ( IsStream t
140− , MonadTrans t
141− , MonadIO m
142− , MonadIO (t (ExceptT String m))
143− , Semigroup (t (ExceptT String m) Integer)
144− )
145− => t (ExceptT String m) ()
146−getSequenceEitherAsyncBelow = do
147− liftIO $ putStrLn "ExceptT below concurrent streamly: "
148−
149− i <- (liftIO (threadDelay 1000)
150− >> lift (throwE "First task")
151− >> return 1)
152− <> (lift (throwE "Second task") >> return 2)
153− <> S.yield (3 :: Integer)
154− liftIO $ putStrLn $ "iteration = " <> show i
155−
156−mainEitherAsyncBelow :: IO ()
157−mainEitherAsyncBelow = do
158− r <- runExceptT (S.drain $ asyncly getSequenceEitherAsyncBelow)
159− case r of
160− Right _ -> liftIO $ putStrLn "Bingo"
161− Left s -> liftIO $ putStrLn s
162−
163−-------------------------------------------------------------------------------
164−-- Using ExceptT above streamly
165−-------------------------------------------------------------------------------
166−--
167−-- When ExceptT is on top, we can lift the non-determinism of stream from
168−-- below.
169−--
170−-- Note that throwE would terminate/break only current iteration of
171−-- non-determinism and not all of them altogether.
172−--
173−-- Here we can use catchE directly but will have to use monad-control to lift
174−-- stream operations with stream arguments.
175−getSequenceEitherAbove :: (IsStream t, MonadIO (t m))
176− => ExceptT String (t m) ()
177−getSequenceEitherAbove = do
178− liftIO $ putStrLn "ExceptT above streamly: Enter one char per line: "
179−
180− i <- lift $ S.fromFoldable [1..2 :: Int]
181− liftIO $ putStrLn $ "iteration = " <> show i
182−
183− r1 <- liftIO getLine
184− when (r1 /= "x") $ throwE $ "Expecting x got: " <> r1
185−
186− r2 <- liftIO getLine
187− when (r2 /= "y") $ throwE $ "Expecting y got: " <> r2
188−
189−mainEitherAbove :: (IsStream t, MonadIO (t m)) => ExceptT String (t m) ()
190−mainEitherAbove =
191− catchE (getSequenceEitherAbove >> liftIO (putStrLn "Bingo"))
192− (liftIO . putStrLn)
193−
194−-------------------------------------------------------------------------------
195−-- Using MonadThrow to throw exceptions in streamly
196−-------------------------------------------------------------------------------
197−--
198−newtype Unexpected = Unexpected String deriving Show
199−
200−instance Exception Unexpected
201−
202−-- Note that unlike when ExceptT is used on top, MonadThrow terminates all
203−-- iterations of non-determinism rather then just the current iteration.
204−--
205−getSequenceMonadThrow :: (IsStream t, MonadIO (t m), MonadThrow (t m))
206− => t m ()
207−getSequenceMonadThrow = do
208− liftIO $ putStrLn "MonadThrow in streamly: Enter one char per line: "
209−
210− i <- S.fromFoldable [1..2 :: Int]
211− liftIO $ putStrLn $ "iteration = " <> show i
212−
213− r1 <- liftIO getLine
214− when (r1 /= "x") $ throwM $ Unexpected $ "Expecting x got: " <> r1
215−
216− r2 <- liftIO getLine
217− when (r2 /= "y") $ throwM $ Unexpected $ "Expecting y got: " <> r2
218−
219−mainMonadThrow :: IO ()
220−mainMonadThrow =
221− catch (S.drain getSequenceMonadThrow >> liftIO (putStrLn "Bingo"))
222− (\(e :: SomeException) -> liftIO $ print e)
223−
224−-------------------------------------------------------------------------------
225−-- Using ContT below streamly
226−-------------------------------------------------------------------------------
227−--
228−-- CallCC is the goto/setjmp/longjmp equivalent
229−-- Allows us to manipulate the control flow in arbitrary ways
230−--
231−-- XXX need to have a specialized liftCallCC to actually lift callCC
232−--
233−getSequenceContBelow
234− :: (IsStream t, MonadTrans t, MonadIO m, MonadIO (t (ContT r m)))
235− => t (ContT r m) (Either String ())
236−getSequenceContBelow = do
237− liftIO $ putStrLn "ContT below streamly: Enter one char per line: "
238−
239− i <- S.fromFoldable [1..2 :: Int]
240− liftIO $ putStrLn $ "iteration = " <> show i
241−
242− r <- lift $ callCC $ \exit -> do
243− r1 <- liftIO getLine
244− _ <- if r1 /= "x"
245− then exit $ Left $ "Expecting x got: " <> r1
246− else return $ Right ()
247−
248− r2 <- liftIO getLine
249− if r2 /= "y"
250− then exit $ Left $ "Expecting y got: " <> r2
251− else return $ Right ()
252− liftIO $ putStrLn $ "done iteration = " <> show i
253− return r
254−
255−mainContBelow
256− :: (IsStream t, MonadIO m, MonadTrans t, MonadIO (t (ContT r m)))
257− => t (ContT r m) ()
258−mainContBelow = do
259− r <- getSequenceContBelow
260− case r of
261− Right _ -> liftIO $ putStrLn "Bingo"
262− Left s -> liftIO $ putStrLn s
263−
264−-------------------------------------------------------------------------------
265−-- Using ContT above streamly
266−-------------------------------------------------------------------------------
267−--
268−getSequenceContAbove :: (IsStream t, MonadIO (t m))
269− => ContT r (t m) (Either String ())
270−getSequenceContAbove = do
271− liftIO $ putStrLn "ContT above streamly: Enter one char per line: "
272−
273− i <- lift $ S.fromFoldable [1..2 :: Int]
274− liftIO $ putStrLn $ "iteration = " <> show i
275−
276− callCC $ \exit -> do
277− r1 <- liftIO getLine
278− _ <- if r1 /= "x"
279− then exit $ Left $ "Expecting x got: " <> r1
280− else return $ Right ()
281−
282− r2 <- liftIO getLine
283− if r2 /= "y"
284− then exit $ Left $ "Expecting y got: " <> r2
285− else return $ Right ()
286−
287−mainContAbove :: (IsStream t, MonadIO (t m)) => ContT r (t m) ()
288−mainContAbove = do
289− r <- getSequenceContAbove
290− case r of
291− Right _ -> liftIO $ putStrLn "Bingo"
292− Left s -> liftIO $ putStrLn s
293−
294−-------------------------------------------------------------------------------
295−-- Combining control flow manipulating monad transformers (MaybeT, exceptT,
296−-- ContT) with Streamly
297−-------------------------------------------------------------------------------
298−
299−main :: IO ()
300−main = do
301− mainMaybeBelow
302− S.drain $ runMaybeT mainMaybeAbove
303− runContT (S.drain mainContBelow) return
304− S.drain (runContT mainContAbove return)
305− mainEitherBelow
306− S.drain (runExceptT mainEitherAbove)
307− mainMonadThrow
308− mainEitherAsyncBelow
examples/EchoServer.hsdeleted−21
@@ -1,21 +0,0 @@
1−-- A concurrent TCP server that echoes everything that it receives.
2−
3−import Data.Function ((&))
4−
5−import Streamly.Internal.Network.Socket (handleWithM)
6−import Streamly.Network.Socket
7−
8−import qualified Streamly.Network.Inet.TCP as TCP
9−import qualified Streamly.Prelude as S
10−
11−main :: IO ()
12−main =
13− S.serially (S.unfold TCP.acceptOnPort 8091)
14− & S.parallely . S.mapM (handleWithM echo)
15− & S.drain
16−
17− where
18−
19− echo sk =
20− S.unfold readChunksWithBufferOf (32768, sk) -- SerialT IO Socket
21− & S.fold (writeChunks sk) -- IO ()
examples/FileIOExamples.hsdeleted−65
@@ -1,65 +0,0 @@
1−import qualified Streamly.Prelude as S
2−import qualified Streamly.Data.Fold as FL
3−import qualified Streamly.Data.Array.Storable.Foreign as A
4−
5−import qualified Streamly.Internal.Data.Fold as FL
6−import qualified Streamly.Internal.Data.Stream.IsStream as IP
7−import qualified Streamly.Internal.FileSystem.File as File
8−
9−import Data.Char (ord)
10−import System.Environment (getArgs)
11−
12−cat :: FilePath -> IO ()
13−cat src =
14− File.fromChunks "/dev/stdout"
15− $ File.toChunksWithBufferOf (256*1024) src
16−
17−cp :: FilePath -> FilePath -> IO ()
18−cp src dst =
19− File.fromChunks dst
20− $ File.toChunksWithBufferOf (256*1024) src
21−
22−append :: FilePath -> FilePath -> IO ()
23−append src dst =
24− File.appendChunks dst
25− $ File.toChunksWithBufferOf (256*1024) src
26−
27−ord' :: Num a => Char -> a
28−ord' = (fromIntegral . ord)
29−
30−wcl :: FilePath -> IO ()
31−wcl src = print =<< (S.length
32− $ S.splitOnSuffix (== ord' '\n') FL.drain
33− $ File.toBytes src)
34−
35−grepc :: String -> FilePath -> IO ()
36−grepc pat src = print . (subtract 1) =<< (S.length
37− $ IP.splitOnSeq (A.fromList (map ord' pat)) FL.drain
38− $ File.toBytes src)
39−
40−avgll :: FilePath -> IO ()
41−avgll src = print =<< (S.fold avg
42− $ S.splitOnSuffix (== ord' '\n') FL.length
43− $ File.toBytes src)
44− where avg = (/) <$> toDouble FL.sum <*> toDouble FL.length
45− toDouble = fmap (fromIntegral :: Int -> Double)
46−
47−llhisto :: FilePath -> IO ()
48−llhisto src = print =<< (S.fold (FL.classify FL.length)
49− $ S.map bucket
50− $ S.splitOnSuffix (== ord' '\n') FL.length
51− $ File.toBytes src)
52− where
53− bucket n = let i = n `mod` 10 in if i > 9 then (9,n) else (i,n)
54−
55−main :: IO ()
56−main = do
57− src <- fmap head getArgs
58−
59− putStrLn "cat" >> cat src -- Unix cat program
60− putStr "wcl " >> wcl src -- Unix wc -l program
61− putStr "grepc " >> grepc "aaaa" src -- Unix grep -c program
62− putStr "avgll " >> avgll src -- get average line length
63− putStr "llhisto " >> llhisto src -- get line length histogram
64− putStr "cp " >> cp src "dst-xyz.txt" -- Unix cp program
65− putStr "append " >> append src "dst-xyz.txt" -- Appending to file
examples/FileSinkServer.hsdeleted−37
@@ -1,37 +0,0 @@
1−-- A concurrent TCP server that:
2−--
3−-- * receives connections from clients
4−-- * splits the incoming data into lines
5−-- * lines from concurrent connections are merged into a single srteam
6−-- * writes the line stream to an output file
7−
8−import Control.Monad.IO.Class (liftIO)
9−import Network.Socket (close)
10−import System.Environment (getArgs)
11−
12−import Streamly.Unicode.Stream
13−import qualified Streamly.FileSystem.Handle as FH
14−import qualified Streamly.Data.Array.Storable.Foreign as A
15−import qualified Streamly.Network.Socket as NS
16−import qualified Streamly.Network.Inet.TCP as TCP
17−import qualified Streamly.Prelude as S
18−
19−import System.IO (withFile, IOMode(..))
20−
21−main :: IO ()
22−main = do
23− file <- fmap head getArgs
24− withFile file AppendMode
25− (\src -> S.fold (FH.write src)
26− $ encodeLatin1
27− $ S.concatUnfold A.read
28− $ S.concatMapWith S.parallel use
29− $ S.unfold TCP.acceptOnPort 8090)
30−
31− where
32−
33− use sk = S.finally (liftIO $ close sk) (recv sk)
34− recv =
35− S.splitWithSuffix (== '\n') A.write
36− . decodeLatin1
37− . S.unfold NS.read
examples/FromFileClient.hsdeleted−20
@@ -1,20 +0,0 @@
1−-- A TCP client that does the following:
2−-- * Reads multiple filenames passed on the command line
3−-- * Opens as many concurrent connections to the server
4−-- * Sends all the files concurrently to the server
5−
6−import System.Environment (getArgs)
7−
8−import qualified Streamly.Prelude as S
9−import qualified Streamly.Internal.FileSystem.Handle as IFH
10−import qualified Streamly.Internal.Network.Inet.TCP as TCP
11−
12−import System.IO (withFile, IOMode(..))
13−
14−main :: IO ()
15−main =
16− let sendFile file =
17− withFile file ReadMode $ \src ->
18− S.fold (TCP.writeChunks (127, 0, 0, 1) 8090)
19− $ IFH.toChunks src
20− in getArgs >>= S.drain . S.parallely . S.mapM sendFile . S.fromList
examples/HandleIO.hsdeleted−107
@@ -1,107 +0,0 @@
1−import Data.Char (ord)
2−import System.Environment (getArgs)
3−import System.IO (IOMode(..), hSeek, SeekMode(..))
4−
5−import qualified Streamly.Data.Fold as FL
6−import qualified Streamly.FileSystem.Handle as FH
7−import qualified System.IO as FH
8−import qualified Streamly.Data.Array.Storable.Foreign as A
9−import qualified Streamly.Prelude as S
10−-- import qualified Streamly.FileSystem.FD as FH
11−
12−import qualified Streamly.Internal.Data.Fold as FL
13−import qualified Streamly.Internal.Unicode.Stream as US
14−import qualified Streamly.Internal.Memory.ArrayStream as AS
15−import qualified Streamly.Internal.Data.Stream.IsStream as S
16−
17−-- Read the contents of a file to stdout.
18−--
19−-- FH.read reads the file in 32KB chunks and converts the chunks into a byte
20−-- stream. FH.write takes the byte stream as input, converts it into chunks of
21−-- 32KB and writes those chunks to stdout.
22−--
23−_cat :: FH.Handle -> IO ()
24−_cat src = S.fold (FH.write FH.stdout) $ S.unfold FH.read src
25−
26−-- Chunked version, more efficient than the byte stream version above. Reads
27−-- the file in 256KB chunks and writes those chunks to stdout.
28−cat :: FH.Handle -> IO ()
29−cat src =
30− S.fold (FH.writeChunks FH.stdout)
31− $ S.unfold FH.readChunksWithBufferOf ((256*1024), src)
32−
33−-- Copy a source file to a destination file.
34−--
35−-- FH.read reads the file in 32KB chunks and converts the chunks into a byte
36−-- stream. FH.write takes the byte stream as input, converts it into chunks of
37−-- 32KB and writes those chunks to the destination file.
38−_cp :: FH.Handle -> FH.Handle -> IO ()
39−_cp src dst = S.fold (FH.write dst) $ S.unfold FH.read src
40−
41−-- Chunked version, more efficient than the byte stream version above. Reads
42−-- the file in 256KB chunks and writes those chunks to stdout.
43−cp :: FH.Handle -> FH.Handle -> IO ()
44−cp src dst =
45− S.fold (FH.writeChunks dst)
46− $ S.unfold FH.readChunksWithBufferOf ((256*1024), src)
47−
48−ord' :: Num a => Char -> a
49−ord' = (fromIntegral . ord)
50−
51−-- Count lines like wc -l.
52−--
53−-- Char stream version. Reads the input as a byte stream, splits it into lines
54−-- and counts the lines..
55−_wcl :: FH.Handle -> IO ()
56−_wcl src = print =<< (S.length
57− $ US.lines FL.drain
58− $ US.decodeLatin1
59− $ S.unfold FH.read src)
60−
61−-- More efficient chunked version. Reads chunks from the input handles and
62−-- splits the chunks directly instead of converting them into byte stream
63−-- first.
64−wcl :: FH.Handle -> IO ()
65−wcl src = print =<< (S.length
66− $ AS.splitOn 10
67− $ S.unfold FH.readChunks src)
68−
69−-- grep -c
70−--
71−-- count the occurrences of a pattern in a file.
72−grepc :: String -> FH.Handle -> IO ()
73−grepc pat src = print . (subtract 1) =<< (S.length
74− $ S.splitOnSeq (A.fromList (map ord' pat)) FL.drain
75− $ S.unfold FH.read src)
76−
77−-- Compute the average line length in a file.
78−avgll :: FH.Handle -> IO ()
79−avgll src = print =<< (S.fold avg
80− $ S.splitWithSuffix (== ord' '\n') FL.length
81− $ S.unfold FH.read src)
82− where avg = (/) <$> toDouble FL.sum <*> toDouble FL.length
83− toDouble = fmap (fromIntegral :: Int -> Double)
84−
85−-- histogram of line lengths in a file
86−llhisto :: FH.Handle -> IO ()
87−llhisto src = print =<< (S.fold (FL.classify FL.length)
88− $ S.map bucket
89− $ S.splitWithSuffix (== ord' '\n') FL.length
90− $ S.unfold FH.read src)
91− where
92− bucket n = let i = n `mod` 10 in if i > 9 then (9,n) else (i,n)
93−
94−main :: IO ()
95−main = do
96− name <- fmap head getArgs
97− src <- FH.openFile name ReadMode
98− let rewind = hSeek src AbsoluteSeek 0
99−
100− rewind >> putStrLn "cat" >> cat src -- Unix cat program
101− rewind >> putStr "wcl " >> wcl src -- Unix wc -l program
102− rewind >> putStr "grepc " >> grepc "aaaa" src -- Unix grep -c program
103− rewind >> putStr "avgll " >> avgll src -- get average line length
104− rewind >> putStr "llhisto " >> llhisto src -- get line length histogram
105−
106− dst <- FH.openFile "dst-xyz.txt" WriteMode
107− rewind >> putStr "cp " >> cp src dst -- Unix cp program
examples/ListDir.hsdeleted−23
@@ -1,23 +0,0 @@
1−module Main (main) where
2−
3−import Data.Bifunctor (bimap)
4−import Data.Function ((&))
5−import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
6−
7−import qualified Streamly.Prelude as S
8−import qualified Streamly.Internal.Data.Stream.IsStream as S
9−import qualified Streamly.Internal.FileSystem.Dir as Dir
10−
11−-- | List the current directory recursively using concurrent processing
12−--
13−main :: IO ()
14−main = do
15− hSetBuffering stdout LineBuffering
16− S.mapM_ print $ S.iterateMapLeftsWith S.ahead listDir (S.yield $ (Left "."))
17−
18− where
19−
20− listDir dir =
21− Dir.toEither dir -- SerialT IO (Either String String)
22− & S.map (bimap prefix prefix) -- SerialT IO (Either String String)
23− where prefix x = dir ++ "/" ++ x
examples/MergeSort.hsdeleted−23
@@ -1,23 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−
3−-- | This example generates two streams sorted in ascending order and merges
4−-- them in ascending order, concurrently.
5−--
6−-- Compile with '-threaded -with-rtsopts "-N"' GHC options to use the
7−-- parallelism.
8−
9−import Data.Word
10−import System.Random (getStdGen, randoms)
11−import Data.List (sort)
12−
13−import Streamly.Prelude (Serial)
14−import qualified Streamly.Prelude as S
15−
16−getSorted :: Serial Word16
17−getSorted = do
18− g <- S.yieldM getStdGen
19− let ls = take 100000 (randoms g) :: [Word16]
20− foldMap return (sort ls)
21−
22−main :: IO ()
23−main = S.last (S.mergeAsyncBy compare getSorted getSorted) >>= print
examples/README.md+2−43
@@ -1,44 +1,3 @@
1−# Running The Examples
1+# Examples
22
3−## Running directly using stack
4−You can run these examples using `stack` like this:
5−
6−```
7−$ stack build
8−$ stack AcidRain.hs
9−```
10−
11−Note: This method may not work for `CirclingSquare.hs` SDL animation example.
12−
13−## Build and run
14−
15−Build the library with the `examples` flag on e.g.
16−
17−```
18−stack build --flag streamly:examples
19−cabal new-build --flags examples
20−```
21−
22−Then run the executables, for example:
23−
24−```
25−stack exec AcidRain
26−```
27−
28−The executable name are the same as the filenames.
29−
30−## Running the SDL animation example
31−
32−To include the SDL examples as well build with `examples-sdl` flag:
33−
34−```
35−stack build --flag streamly:examples-sdl
36−cabal new-build --flags examples-sdl
37−```
38−
39−Make sure that you have the SDL OS package installed on your system and the
40−headers are visible to Haskell build tool.
41−
42−```
43−stack exec CirclingSquare
44−```
3+Examples are available in [streamly-examples][https://github.com/composewell/streamly-examples] repo.
examples/Rate.hsdeleted−10
@@ -1,10 +0,0 @@
1−import qualified Streamly.Prelude as S
2−import qualified Streamly.Internal.Data.Stream.IsStream as Internal
3−
4−main :: IO ()
5−main =
6− S.mapM_ print
7− $ S.asyncly
8− $ S.avgRate 1
9− $ Internal.timestamped
10− $ S.repeatM (pure "tick")
examples/SearchQuery.hsdeleted−35
@@ -1,35 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−
3−#if !(MIN_VERSION_base(4,11,0))
4−import Data.Semigroup ((<>))
5−#endif
6−import Streamly.Prelude (drain, nil, yieldM, (|:))
7−import Network.HTTP.Simple
8−
9−import qualified Streamly.Prelude as S
10−
11−-- | Runs three search engine queries in parallel and prints the search engine
12−-- names in the fastest first order.
13−--
14−-- Does it twice using two different ways.
15−--
16−main :: IO ()
17−main = do
18− putStrLn "Using parallel stream construction"
19− drain . S.parallely $ google |: bing |: duckduckgo |: nil
20−
21− putStrLn "\nUsing parallel semigroup composition"
22− drain . S.parallely $ yieldM google <> yieldM bing <> yieldM duckduckgo
23−
24− putStrLn "\nUsing parallel applicative zip"
25− drain . S.zipAsyncly $
26− (,,) <$> yieldM google <*> yieldM bing <*> yieldM duckduckgo
27−
28− where
29− get :: String -> IO ()
30− get s = httpNoBody (parseRequest_ s) >> print s
31−
32− google, bing, duckduckgo :: IO ()
33− google = get "https://www.google.com/search?q=haskell"
34− bing = get "https://www.bing.com/search?q=haskell"
35− duckduckgo = get "https://www.duckduckgo.com/?q=haskell"
examples/Split.hsdeleted−38
@@ -1,38 +0,0 @@
1−import qualified Streamly.Internal.Data.Stream.IsStream as S
2−import qualified Streamly.Internal.FileSystem.Handle as IFH
3−import qualified Streamly.FileSystem.Handle as FH
4−import qualified System.IO as FH
5−
6−import Control.Monad.IO.Class (liftIO)
7−import Control.Monad.State.Strict (StateT(..), get, put)
8−import System.Environment (getArgs)
9−import System.IO (IOMode(..))
10−import Data.Function ((&))
11−
12−newHandle :: StateT (Maybe (FH.Handle, Int)) IO FH.Handle
13−newHandle = do
14− old <- get
15− idx <- case old of
16− Nothing -> return 0
17− Just (h, i) -> liftIO (FH.hClose h) >> return (i + 1)
18− h <- liftIO $ FH.openFile ("dst-xyz-" ++ show idx ++ ".txt") WriteMode
19− put (Just (h, idx))
20− return h
21−
22−-- XXX reduce the input stream to a stream of file names
23−-- The fold can return the file name/handle after it is done.
24−-- similarly the files can written to directories and we can generate a stream
25−-- of directory names.
26−splitFile :: FH.Handle -> IO ()
27−splitFile inHandle =
28− S.unfold FH.read inHandle
29− & S.liftInner
30− & S.chunksOf2 (180 * 1024 * 1024) newHandle IFH.write2
31− & S.evalStateT (return Nothing) -- generate new handle for each iteration
32− & S.drain
33−
34−main :: IO ()
35−main = do
36− name <- fmap head getArgs
37− src <- FH.openFile name ReadMode
38− splitFile src
examples/WordClassifier.hsdeleted−75
@@ -1,75 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−
4−{-# OPTIONS_GHC -Wno-orphans #-}
5−
6−-- compile with:
7−-- ghc -O2 -fspec-constr-recursive=10 -fmax-worker-args=16 word-classifier.hs
8−--
9−import qualified Data.Char as Char
10−import Data.Foldable
11−import Data.Function ((&))
12−import Data.Functor.Identity (Identity(..))
13−import qualified Data.HashMap.Strict as Map
14−import Data.Hashable
15−import Data.IORef
16−import qualified Data.List as List
17−import qualified Data.Ord as Ord
18−import Foreign.Storable (Storable(..))
19−import qualified Streamly.Unicode.Stream as S
20−import qualified Streamly.Internal.Unicode.Stream as S
21−import qualified Streamly.Data.Fold as FL
22−import qualified Streamly.Internal.Data.Fold as IFL
23−import qualified Streamly.Internal.Data.Unfold as IUF
24−import qualified Streamly.Internal.FileSystem.File as File
25−import qualified Streamly.Data.Array.Storable.Foreign as A
26−import qualified Streamly.Prelude as S
27−import System.Environment (getArgs)
28−
29−instance (Enum a, Storable a) => Hashable (A.Array a) where
30− hash arr = fromIntegral $ runIdentity $ IUF.fold A.read IFL.rollingHash arr
31− hashWithSalt salt arr = fromIntegral $ runIdentity $
32− IUF.fold A.read (IFL.rollingHashWithSalt $ fromIntegral salt) arr
33−
34−{-# INLINE toLower #-}
35−toLower :: Char -> Char
36−toLower c
37− | uc >= 0x61 && uc <= 0x7a = c
38− | otherwise = Char.toLower c
39− where
40− uc = fromIntegral (Char.ord c) :: Word
41−
42−{-# INLINE isAlpha #-}
43−isAlpha :: Char -> Bool
44−isAlpha c
45− | uc >= 0x61 && uc <= 0x7a = True
46− | otherwise = Char.isAlpha c
47− where
48− uc = fromIntegral (Char.ord c) :: Word
49−
50−main :: IO ()
51−main = do
52− inFile <- fmap head getArgs
53−
54− -- Write the stream to a hashmap consisting of word counts
55− mp <-
56− let
57− alter Nothing = fmap Just $ newIORef (1 :: Int)
58− alter (Just ref) = modifyIORef' ref (+ 1) >> return (Just ref)
59− in File.toBytes inFile -- SerialT IO Word8
60− & S.decodeLatin1 -- SerialT IO Char
61− & S.map toLower -- SerialT IO Char
62− & S.words FL.toList -- SerialT IO String
63− & S.filter (all isAlpha) -- SerialT IO String
64− & S.foldlM' (flip (Map.alterF alter)) (return Map.empty) -- IO (Map String (IORef Int))
65−
66− -- Print the top hashmap entries
67− counts <-
68− let readRef (w, ref) = do
69− cnt <- readIORef ref
70− return (w, cnt)
71− in Map.toList mp
72− & mapM readRef
73−
74− traverse_ print $ List.sortOn (Ord.Down . snd) counts
75− & List.take 25
examples/WordCount.hsdeleted−628
@@ -1,628 +0,0 @@
1−-------------------------------------------------------------------------------
2−-- Fast, streaming and parallel word counting (wc) program.
3−-------------------------------------------------------------------------------
4−-- 1) On utf8 inputs the serial version is around 3x faster than MacOS wc
5−-- 2) It can run parallely on multiple cores providing further speedup
6−-- 3) Parallel version works efficiently on stdin/streaming input as well
7−-- 4) Parallel version handles utf8 input correctly (including multi-byte space
8−-- chars) and gives the same output as the serial version on all inputs.
9−-- 5) There may be differences in word/char counts when there are invalid utf8
10−-- byte sequences present in the input because of different styles of error
11−-- handling.
12−
13−-------------------------------------------------------------------------------
14−-- Build with the following options:
15−-------------------------------------------------------------------------------
16−-- streamly optimization plugin is required for best performance
17−-- ghc -O2 -fplugin Plugin -fspec-constr-recursive=10 -fmax-worker-args=16
18−-- For concurrent version add: -threaded -with-rtsopts "-N"
19−
20−-------------------------------------------------------------------------------
21−-- Comparing with "wc -mwl" command:
22−-------------------------------------------------------------------------------
23−--
24−-- 1) To enable UTF8 with wc: export LANG=en_US.UTF-8; export LC_ALL=$LANG
25−-- 2) To test whether it is acutally using utf8, copy and paste this string
26−-- "U+1680 U+2000 U+2001 U+2002" and run "wc -mwl" on this. Without proper UTF8
27−-- handling word count would be 1, with proper UTF8 handling word count would
28−-- be 4. Note that the spaces in this string are not regular space chars they
29−-- are different unicode space chars.
30−
31−{-# LANGUAGE CPP #-}
32−
33−import Control.Monad (when)
34−import Data.Char (isSpace)
35−import Data.Word (Word8)
36−import GHC.Conc (numCapabilities)
37−import System.Environment (getArgs)
38−import System.IO (Handle, openFile, IOMode(..))
39−import Streamly.Internal.Unicode.Stream
40− (DecodeState, DecodeError(..), CodePoint, decodeUtf8Either,
41− resumeDecodeUtf8Either)
42−
43−import qualified Streamly.Internal.Data.Stream.IsStream as S
44−import qualified Streamly.Unicode.Stream as S
45−import qualified Streamly.FileSystem.Handle as FH
46−import qualified Streamly.Data.Array.Storable.Foreign as A
47−import qualified Data.Vector.Storable.Mutable as V
48−
49−-------------------------------------------------------------------------------
50−-- Parallel char, line and word counting
51−-------------------------------------------------------------------------------
52−
53−-- We process individual chunks in the stream independently and parallely and
54−-- the combine the chunks to combine what they have counted.
55−--
56−-------------------------------------------------------------------------------
57−-- Char counting
58−-------------------------------------------------------------------------------
59−
60−-- To count chars each block needs the following:
61−--
62−-- -- | header | char counts | trailer |
63−--
64−-- header and trailer are incomplete utf8 byte sequences that may be combined
65−-- with the previous or the next block to complete them later.
66−--
67−-- The trailer may have one or more bytes in a valid utf8 sequence and is
68−-- expecting more bytes to complete the sequence. The header stores any
69−-- possible continuation from the previous block. It contains a maximum of 3
70−-- bytes which all must be non-starter bytes.
71−--
72−-- When two blocks are combined, the trailer of the first block is combined
73−-- with the header of the next block and then utf8 decoded. The combined
74−-- header+trailer may yield:
75−--
76−-- * Nothing - when there is no trailer and header
77−-- * All errors - when there is no trailer in the previous block, and there is
78−-- a header in the next block. In this case there is no starting char which
79−-- means all header bytes are errors.
80−-- * It can yield at most one valid character followed by 0, 1 or 2 errors.
81−--
82−-- We count an incomplete utf8 sequence of 2 or more bytes starting with a
83−-- valid starter byte as a single codepoint. Bytes not following a valid
84−-- starter byte are treated as individual codepoints for counting.
85−--
86−-------------------------------------------------------------------------------
87−-- Word counting
88−-------------------------------------------------------------------------------
89−
90−-- For word counting we need the following in each block:
91−--
92−-- -- | header | startsWithSpace | word counts | endsWithSpace | trailer |
93−--
94−-- The word counts in individual blocks are performed assuming that the
95−-- previous char before the block is a space.
96−-- When combining two blocks, after combining the trailer of previous blocks
97−-- with the header of the next we determine if the resulting char is a space or
98−-- not.
99−--
100−-- 1) If there is no new char joining the two blocks then we use endsWithSpace
101−-- of the previous block and startsWithSpace of the next block to determine if
102−-- the word counts are to be adjusted. If the previous block ends with
103−-- non-space and the next block starts with non-space we need to decrement the
104−-- word count by one if it is non-zero in the next block.
105−--
106−-- 2) If the new joining char is a space then we combine it with
107−-- startsWithSpace and endsWithSpace to determine the
108−-- startsWithSpace/endsWithSpace of the combined block and adjust the word
109−-- counts appropriately.
110−--
111−-------------------------------------------------------------------------------
112−-- Line counting
113−-------------------------------------------------------------------------------
114−
115−-- Line counting is performed by counting "\n" in the stream. No new "\n" can
116−-- result from patching the trailer and header as it is always a single byte.
117−
118−-------------------------------------------------------------------------------
119−-- Counting state
120−-------------------------------------------------------------------------------
121−
122−-- We use a mutable vector for the counting state. A fold using an immutable
123−-- structure for such a large state does not perform well. However, mutability
124−-- is confined to just the accumulator.
125−--
126−-- XXX we need convenient mutable records (like C structs) to handle things
127−-- like this. It may be possible to achieve the same performance with an
128−-- immutable accumulator, but that will require more research. Since we are
129−-- always discarding the previous state, we can perhaps make use of that memory
130−-- using safe in-place modifications, without having to allocate new memory.
131−
132−-- XXX we can also count the number of decoding errors separately
133−data Field =
134− -- The number of "\n" characters found in the block.
135− LineCount
136− -- Number of full words found in the block, words are counted on a
137− -- transition from space char to a non-space char. We always assume the
138− -- char before the first starter char in a block is a space. If this is
139− -- found to be incorrect when joining two blocks then we fix the counts
140− -- accordingly.
141− | WordCount
142− -- The number of successfully decoded characters plus the number of
143− -- decoding failures in the block. Each byte or sequence of bytes on which
144− -- decoding fails is also counted as one char. The header and trailer bytes
145− -- are not accounted in this, they are accounted only when we join two
146− -- blocks.
147− | CharCount
148− -- whether the last counted char in this block was a space char
149− | WasSpace
150− -- whether the first successfully decoded char in this block is a space. A
151− -- decoding failure, after the trailing bytes from the previous block are
152− -- accounted, is also considered as space.
153− | FirstIsSpace
154− -- If no starter byte is found in the first three bytes in the block then
155− -- store those bytes to possibly combine them with the trailing incomplete
156− -- byte sequence in the previous block. We mark it done when either we have
157− -- stored three bytes or we have found a starter byte.
158− --
159− -- XXX This is ugly to manipulate, we can implement a statically max sized
160− -- mutable ring structure within this record.
161− | HeaderDone
162− | HeaderWordCount
163− | HeaderWord1
164− | HeaderWord2
165− | HeaderWord3
166− -- If a byte sequence at the end of the block is not complete then store
167− -- the current state of the utf8 decoder to continue it later using the
168− -- incomplete leading byte sequence in the next block.
169− | TrailerPresent
170− | TrailerState
171− | TrailerCodePoint
172− deriving (Show, Enum, Bounded)
173−
174−-------------------------------------------------------------------------------
175−-- Default/initial state of the block
176−-------------------------------------------------------------------------------
177−
178−readField :: V.IOVector Int -> Field -> IO Int
179−readField v fld = V.read v (fromEnum fld)
180−
181−writeField :: V.IOVector Int -> Field -> Int -> IO ()
182−writeField v fld val = V.write v (fromEnum fld) val
183−
184−modifyField :: V.IOVector Int -> Field -> (Int -> Int) -> IO ()
185−modifyField v fld f = V.modify v f (fromEnum fld)
186−
187−newCounts :: IO (V.IOVector Int)
188−newCounts = do
189− counts <- V.new (fromEnum (maxBound :: Field) + 1)
190− writeField counts LineCount 0
191− writeField counts WordCount 0
192− writeField counts CharCount 0
193− writeField counts WasSpace 1
194− writeField counts FirstIsSpace 0
195− writeField counts HeaderDone 0
196− writeField counts HeaderWordCount 0
197− writeField counts TrailerPresent 0
198− return counts
199−
200−-------------------------------------------------------------------------------
201−-- Counting chars
202−-------------------------------------------------------------------------------
203−
204−accountChar :: V.IOVector Int -> Bool -> IO ()
205−accountChar counts isSp = do
206− c <- readField counts CharCount
207− let space = if isSp then 1 else 0
208− when (c == 0) $ writeField counts FirstIsSpace space
209− writeField counts CharCount (c + 1)
210− writeField counts WasSpace space
211−
212−-------------------------------------------------------------------------------
213−-- Manipulating the header bytes
214−-------------------------------------------------------------------------------
215−
216−addToHeader :: V.IOVector Int -> Int -> IO Bool
217−addToHeader counts cp = do
218− cnt <- readField counts HeaderWordCount
219− case cnt of
220− 0 -> do
221− writeField counts HeaderWord1 cp
222− writeField counts HeaderWordCount 1
223− return True
224− 1 -> do
225− writeField counts HeaderWord2 cp
226− writeField counts HeaderWordCount 2
227− return True
228− 2 -> do
229− writeField counts HeaderWord3 cp
230− writeField counts HeaderWordCount 3
231− writeField counts HeaderDone 1
232− return True
233− _ -> return False
234−
235−resetHeaderOnNewChar :: V.IOVector Int -> IO ()
236−resetHeaderOnNewChar counts = do
237− hdone <- readField counts HeaderDone
238− when (hdone == 0) $ writeField counts HeaderDone 1
239−
240−-------------------------------------------------------------------------------
241−-- Manipulating the trailer
242−-------------------------------------------------------------------------------
243−
244−setTrailer :: V.IOVector Int -> DecodeState -> CodePoint -> IO ()
245−setTrailer counts st cp = do
246− writeField counts TrailerState (fromIntegral st)
247− writeField counts TrailerCodePoint cp
248− writeField counts TrailerPresent 1
249−
250−resetTrailerOnNewChar :: V.IOVector Int -> IO ()
251−resetTrailerOnNewChar counts = do
252− trailer <- readField counts TrailerPresent
253− when (trailer /= 0) $ do
254− writeField counts TrailerPresent 0
255− accountChar counts True
256−
257−-------------------------------------------------------------------------------
258−-- Counting the stream
259−-------------------------------------------------------------------------------
260−
261−{-# INLINE countChar #-}
262−countChar :: V.IOVector Int -> Either DecodeError Char -> IO ()
263−countChar counts inp =
264− case inp of
265− Right ch -> do
266− resetHeaderOnNewChar counts
267− -- account the last stored error as whitespace and clear it
268− resetTrailerOnNewChar counts
269−
270− when (ch == '\n') $ modifyField counts LineCount (+ 1)
271− if isSpace ch
272− then accountChar counts True
273− else do
274− wasSpace <- readField counts WasSpace
275− when (wasSpace /= 0) $ modifyField counts WordCount (+ 1)
276− accountChar counts False
277− Left (DecodeError st cp) -> do
278− hdone <- readField counts HeaderDone
279− if hdone == 0
280− then do
281− if st == 0
282− then do
283− -- We got a non-starter in initial decoder state, there may
284− -- be something that comes before this to complete it.
285− r <- addToHeader counts cp
286− when (not r) $ error "countChar: Bug addToHeader failed"
287− else do
288− -- We got an error in a non-initial decoder state, it may
289− -- be an input underflow error, keep it as incomplete in
290− -- the trailer.
291− writeField counts HeaderDone 1
292− setTrailer counts st cp
293− else do
294− resetTrailerOnNewChar counts
295− if st == 0
296− then accountChar counts True
297− else setTrailer counts st cp
298−
299−printCounts :: V.IOVector Int -> IO ()
300−printCounts v = do
301− l <- readField v LineCount
302− w <- readField v WordCount
303− c <- readField v CharCount
304− putStrLn $ show l ++ " " ++ show w ++ " " ++ show c
305−
306−-------------------------------------------------------------------------------
307−-- Serial counting using parallel version of countChar
308−-------------------------------------------------------------------------------
309−
310−_wc_mwl_parserial :: Handle -> IO (V.IOVector Int)
311−_wc_mwl_parserial src = do
312− counts <- newCounts
313− S.mapM_ (countChar counts)
314− $ decodeUtf8Either
315− $ S.unfold FH.read src
316− return counts
317−
318−-------------------------------------------------------------------------------
319−-- Serial word counting with UTF-8 handling
320−-------------------------------------------------------------------------------
321−
322−data Counts = Counts !Int !Int !Int !Bool deriving Show
323−
324−{-# INLINE countCharSerial #-}
325−countCharSerial :: Counts -> Char -> Counts
326−countCharSerial (Counts l w c wasSpace) ch =
327− let l1 = if (ch == '\n') then l + 1 else l
328− (w1, wasSpace1) =
329− if (isSpace ch)
330− then (w, True)
331− else (if wasSpace then w + 1 else w, False)
332− in (Counts l1 w1 (c + 1) wasSpace1)
333−
334−-- Note: This counts invalid byte sequences are non-space chars
335−_wc_mwl_serial :: Handle -> IO ()
336−_wc_mwl_serial src = print =<< (
337− S.foldl' countCharSerial (Counts 0 0 0 True)
338− $ S.decodeUtf8
339− $ S.unfold FH.read src)
340−
341−-------------------------------------------------------------------------------
342−-- Parallel counting
343−-------------------------------------------------------------------------------
344−
345−-- XXX we need a better data structure to store the header bytes to make these
346−-- routines simpler.
347−--
348−-- combine trailing bytes in preceding block with leading bytes in the next
349−-- block and decode them into a codepoint
350−reconstructChar :: Int
351− -> V.IOVector Int
352− -> V.IOVector Int
353− -> IO (S.SerialT IO (Either DecodeError Char))
354−reconstructChar hdrCnt v1 v2 = do
355− when (hdrCnt > 3 || hdrCnt < 0) $ error "reconstructChar: hdrCnt > 3"
356− stream1 <-
357− if (hdrCnt > 2)
358− then do
359− x <- readField v2 HeaderWord3
360− return $ (fromIntegral x :: Word8) `S.cons` S.nil
361− else return S.nil
362− stream2 <-
363− if (hdrCnt > 1)
364− then do
365− x <- readField v2 HeaderWord2
366− return $ fromIntegral x `S.cons` stream1
367− else return stream1
368− stream3 <-
369− if (hdrCnt > 0)
370− then do
371− x <- readField v2 HeaderWord1
372− return $ fromIntegral x `S.cons` stream2
373− else return stream2
374−
375− state <- readField v1 TrailerState
376− cp <- readField v1 TrailerCodePoint
377− return $ resumeDecodeUtf8Either (fromIntegral state) cp stream3
378−
379−getHdrChar :: V.IOVector Int -> IO (Maybe Int)
380−getHdrChar v = do
381− hdrCnt <- readField v HeaderWordCount
382− case hdrCnt of
383− 0 -> return Nothing
384− 1 -> do
385− writeField v HeaderWordCount 0
386− fmap Just $ readField v HeaderWord1
387− 2 -> do
388− x1 <- readField v HeaderWord1
389− x2 <- readField v HeaderWord2
390− writeField v HeaderWord1 x2
391− writeField v HeaderWordCount 1
392− return $ Just x1
393− 3 -> do
394− x1 <- readField v HeaderWord1
395− x2 <- readField v HeaderWord2
396− x3 <- readField v HeaderWord3
397− writeField v HeaderWord1 x2
398− writeField v HeaderWord2 x3
399− writeField v HeaderWordCount 2
400− return $ Just x1
401− _ -> error "getHdrChar: Bug, hdrCnt not in range 0-3"
402−
403−-- If the header of the first block is not done then combine the header
404−-- with the header of the next block.
405−combineHeaders :: V.IOVector Int -> V.IOVector Int -> IO ()
406−combineHeaders v1 v2 = do
407− hdone1 <- readField v1 HeaderDone
408− if hdone1 == 0
409− then do
410− res <- getHdrChar v2
411− case res of
412− Nothing -> return ()
413− Just x -> do
414− r <- addToHeader v1 x
415− when (not r) $ error "combineHeaders: Bug, addToHeader failed"
416− else return ()
417−
418−-- We combine the contents of the second vector into the first vector, mutating
419−-- the first vector and returning it.
420−-- XXX This is a quick hack and can be refactored to reduce the size
421−-- and understandability considerably.
422−addCounts :: V.IOVector Int -> V.IOVector Int -> IO (V.IOVector Int)
423−addCounts v1 v2 = do
424− hdone1 <- readField v1 HeaderDone
425− hdone2 <- readField v2 HeaderDone
426− hdrCnt2_0 <- readField v2 HeaderWordCount
427− if hdone1 == 0 && (hdrCnt2_0 /= 0 || hdone2 /= 0)
428− then do
429− combineHeaders v1 v2
430− if hdone2 == 0
431− then return v1
432− else do
433− writeField v1 HeaderDone 1
434− addCounts v1 v2
435− else do
436− trailerPresent2 <- readField v2 TrailerPresent
437− trailerState2 <- readField v2 TrailerState
438− trailerCodePoint2 <- readField v2 TrailerCodePoint
439− if hdone1 == 0
440− then error "addCounts: Bug, trying to add completely empty second block"
441− else do
442− l1 <- readField v1 LineCount
443− w1 <- readField v1 WordCount
444− c1 <- readField v1 CharCount
445− wasSpace1 <- readField v1 WasSpace
446−
447− l2 <- readField v2 LineCount
448− w2 <- readField v2 WordCount
449− c2 <- readField v2 CharCount
450− wasSpace2 <- readField v2 WasSpace
451− firstIsSpace2 <- readField v2 FirstIsSpace
452− hdrCnt2 <- readField v2 HeaderWordCount
453−
454− trailer1 <- readField v1 TrailerPresent
455− if trailer1 == 0 -- no trailer in the first block
456− then do
457− -- header2, if any, complete or incomplete, is just invalid
458− -- bytes, count them as whitespace
459− let firstIsSpace2' = firstIsSpace2 /= 0 || hdrCnt2 /= 0
460− w <-
461− if w2 /= 0 && wasSpace1 == 0 && not firstIsSpace2'
462− then return $ w1 + w2 - 1
463− else return $ w1 + w2
464− writeField v1 LineCount (l1 + l2)
465− writeField v1 WordCount w
466− writeField v1 CharCount (c1 + c2 + hdrCnt2)
467−
468− when (c1 == 0) $ do
469− if c2 == 0 && hdrCnt2 /= 0
470− then writeField v1 FirstIsSpace 1
471− else writeField v1 FirstIsSpace firstIsSpace2
472−
473− if c2 == 0 && hdrCnt2 /= 0
474− then writeField v1 WasSpace 1
475− else writeField v1 WasSpace wasSpace2
476−
477− writeField v1 TrailerPresent trailerPresent2
478− writeField v1 TrailerState trailerState2
479− writeField v1 TrailerCodePoint trailerCodePoint2
480− return v1
481− else do
482− if hdrCnt2 == 0
483− then do
484− when (hdone2 /= 0) $ do -- empty and Done header
485− -- count trailer as whitespace, its counted as one char
486− -- Note: hdrCnt2 == 0 means either header is not done
487− -- or c2 /= 0
488− writeField v1 LineCount (l1 + l2)
489− writeField v1 WordCount (w1 + w2)
490− writeField v1 CharCount (c1 + c2 + 1)
491−
492− when (c1 == 0) $ writeField v1 FirstIsSpace 1
493− if (c2 == 0)
494− then writeField v1 WasSpace 1
495− else writeField v1 WasSpace wasSpace2
496−
497− writeField v1 TrailerPresent trailerPresent2
498− writeField v1 TrailerState trailerState2
499− writeField v1 TrailerCodePoint trailerCodePoint2
500− -- If header of the next block is not done we continue the
501− -- trailer from the previous block instead of treating it
502− -- as whitespace
503− return v1
504− else do
505− -- join the trailing part of the first block with the
506− -- header of the next
507− decoded <- reconstructChar hdrCnt2 v1 v2
508− res <- S.uncons decoded
509− case res of
510− Nothing -> error "addCounts: Bug. empty reconstructed char"
511− Just (h, t) -> do
512− tlength <- S.length t
513− case h of
514− Right ch -> do
515− -- If we have an error case after this
516− -- char then that would be treated
517− -- as whitespace
518− let lcount = l1 + l2
519− lcount1 = if (ch == '\n') then lcount + 1 else lcount
520− wcount = w1 + w2
521− firstSpace = isSpace ch
522− wasSpace = firstSpace || tlength /= 0
523− wcount1 =
524− if wasSpace
525− then wcount
526− else if w2 == 0 || firstIsSpace2 /= 0
527− then wcount
528− else wcount - 1
529− writeField v1 LineCount lcount1
530− writeField v1 WordCount wcount1
531− writeField v1 CharCount (c1 + c2 + 1 + tlength)
532−
533− when (c1 == 0) $
534− writeField v1 FirstIsSpace
535− (if firstSpace then 1 else 0)
536−
537− if c2 == 0
538− then do
539− if wasSpace
540− then writeField v1 WasSpace 1
541− else writeField v1 WasSpace 0
542− else writeField v1 WasSpace wasSpace2
543−
544− writeField v1 TrailerPresent trailerPresent2
545− writeField v1 TrailerState trailerState2
546− writeField v1 TrailerCodePoint trailerCodePoint2
547− return v1
548− Left (DecodeError st cp) -> do
549− -- if header was incomplete it may result
550− -- in partially decoded char to be written
551− -- as trailer. Check if the last error is
552− -- an incomplete decode.
553− r <- S.last t
554− let (st', cp') =
555− case r of
556− Nothing -> (st, cp)
557− Just lst -> case lst of
558− Right _ -> error "addCounts: Bug"
559− Left (DecodeError st1 cp1) -> (st1, cp1)
560− if hdone2 == 0 && st' /= 12
561− then do
562− -- all elements before the last one must be errors
563− writeField v1 CharCount (c1 + tlength)
564−
565− when (c1 == 0) $
566− writeField v1 FirstIsSpace 1
567−
568− writeField v1 WasSpace 1
569−
570− writeField v1 TrailerState (fromIntegral st')
571− writeField v1 TrailerCodePoint cp'
572− else do
573− -- all elements must be errors
574− -- treat them as whitespace
575− writeField v1 LineCount (l1 + l2)
576− writeField v1 WordCount (w1 + w2)
577− writeField v1 CharCount (c1 + c2 + tlength + 1)
578−
579− when (c1 == 0) $
580− writeField v1 FirstIsSpace 1
581−
582− if c2 == 0
583− then writeField v1 WasSpace 1
584− else writeField v1 WasSpace wasSpace2
585−
586− writeField v1 TrailerPresent trailerPresent2
587− writeField v1 TrailerState trailerState2
588− writeField v1 TrailerCodePoint trailerCodePoint2
589− return v1
590−
591−-- Individual array processing is an isolated loop, fusing it with the bigger
592−-- loop may be counter productive.
593−{-# NOINLINE countArray #-}
594−countArray :: A.Array Word8 -> IO (V.IOVector Int)
595−countArray src = do
596− counts <- newCounts
597− S.mapM_ (countChar counts)
598− $ decodeUtf8Either
599− $ S.unfold A.read src
600− return counts
601−
602−{-# INLINE wc_mwl_parallel #-}
603−wc_mwl_parallel :: Handle -> Int -> IO (V.IOVector Int)
604−wc_mwl_parallel src n = do
605− S.foldlM' addCounts newCounts
606− $ S.aheadly
607− $ S.maxThreads numCapabilities
608− $ S.mapM (countArray)
609− $ S.unfold FH.readChunksWithBufferOf (n, src)
610−
611−-------------------------------------------------------------------------------
612−-- Main
613−-------------------------------------------------------------------------------
614−
615−main :: IO ()
616−main = do
617− name <- fmap head getArgs
618− src <- openFile name ReadMode
619− -- _wc_mwl_serial src -- Unix wc -l program
620− -- printCounts =<< _wc_mwl_parserial src -- Unix wc -l program
621− -- Using different sizes of chunks (1,2,3,4,5,10,128,256) is a good testing
622− -- mechanism for parallel counting code.
623− {-
624− args <- getArgs
625− let chunkSize = read $ args !! 1
626− -}
627− let chunkSize = 32 * 1024
628− printCounts =<< wc_mwl_parallel src chunkSize -- Unix wc -l program
src/Streamly/Tutorial.hs+16−14
@@ -24,7 +24,8 @@
2424-- used to fine tune the concurrency control.
2525--
2626-- Streaming and concurrency together enable expressing reactive applications
27−-- conveniently. See the @CirclingSquare@ example in the examples directory for
27+-- conveniently. See the @CirclingSquare@ example in
28+-- <https://github.com/composewell/streamly-examples Streamly Examples> for
2829-- a simple SDL based FRP example. To summarize, streamly provides a unified
2930-- computing framework for streaming, non-determinism and functional reactive
3031-- programming in an elegant and simple API that is a natural extension of pure
@@ -912,7 +913,7 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
912913-- time. Take an example of a merge sort of two sorted streams. We need to
913914-- keep consuming items from the stream which has the lowest item in the sort
914915-- order. This can be achieved using async references to streams. See
915−-- "MergeSort.hs" in the examples directory.
916+-- "MergeSort.hs" in <https://github.com/composewell/streamly-examples Streamly Examples>.
916917
917918-- $monoid
918919--
@@ -1436,9 +1437,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
14361437-- and operators instead of the ugly pragmas.
14371438--
14381439-- For more concurrent programming examples see,
1439−-- <examples/ListDir.hs ListDir.hs>,
1440−-- <examples/MergeSort.hs MergeSort.hs> and
1441−-- <examples/SearchQuery.hs SearchQuery.hs> in the examples directory.
1440+-- <https://github.com/composewell/streamly-examples/tree/master/ListDir.hs ListDir.hs>,
1441+-- <https://github.com/composewell/streamly-examples/tree/master/MergeSort.hs MergeSort.hs> and
1442+-- <https://github.com/composewell/streamly-examples/tree/master/SearchQuery.hs SearchQuery.hs> in the examples directory.
14421443
14431444-- $reactive
14441445--
@@ -1512,14 +1513,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
15121513-- void $ runStateT runGame 60
15131514-- @
15141515--
1515−-- You can also find the source of this example in the examples directory as
1516−-- <examples/AcidRain.hs AcidRain.hs>. It has been adapted from Gabriel's
1516+-- You can also find the source of this example in the streamly-examples repo
1517+-- as <https://github.com/composewell/streamly-examples/tree/master/AcidRain.hs AcidRain.hs>.
1518+-- It has been adapted from Gabriel's
15171519-- <https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html pipes-concurrency>
15181520-- package.
15191521-- This is much simpler compared to the pipes version because of the builtin
15201522-- concurrency in streamly. You can also find a SDL based reactive programming
15211523-- example adapted from Yampa in
1522−-- <examples/CirclingSquare.hs CirclingSquare.hs>.
1524+-- <https://github.com/composewell/streamly-examples/tree/master/CirclingSquare.hs CirclingSquare.hs>.
15231525
15241526-- $performance
15251527--
@@ -1714,15 +1716,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
17141716-- strong reactive programming library as well. Reactive programming is
17151717-- fundamentally stream of events that can be processed concurrently. The
17161718-- example in this tutorial as well as the
1717−-- <examples/CirclingSquare.hs CirclingSquare> example from Yampa demonstrate
1718−-- the basic reactive capability of streamly. In core concepts streamly is
1719−-- strikingly similar to @dunai@. dunai was designed from a FRP perspective
1720−-- and streamly was originally designed from a concurrency perspective.
1721−-- However, both have similarity at the core.
1719+-- <https://github.com/composewell/streamly-examples/tree/master/CirclingSquare.hs CirclingSquare>
1720+-- example from Yampa demonstrate the basic reactive capability of streamly.
1721+-- In core concepts streamly is strikingly similar to @dunai@. dunai was
1722+-- designed from a FRP perspective and streamly was originally designed from a
1723+-- concurrency perspective. However, both have similarity at the core.
17221724
17231725-- $furtherReading
17241726--
17251727-- * Read the documentation of "Streamly" module
17261728-- * Read the documentation of "Streamly.Prelude" module
1727−-- * See the examples in the "examples" directory of the package
1729+-- * See the examples in <https://github.com/composewell/streamly-examples streamly-examples> repo.
17281730-- * See the tests in the "test" directory of the package
streamly.cabal+1−220
@@ -68,7 +68,7 @@ description:
6868 * /Interoperation/: "Streamly.Tutorial" module for interop with other
6969 streaming libraries
7070 * /Reference Documentation/: Haddock documentation for the respective modules
71− * /Examples/: <src/examples examples directory> in the package
71+ * /Examples/: <https://github.com/composewell/streamly-examples Streamly Examples>
7272 * /Guides/: <src/docs docs directory> in the package, for documentation on
7373 advanced topics, limitations, semantics of the library or on specific use
7474 cases.
@@ -197,16 +197,6 @@ flag streamk
197197 manual: True
198198 default: False
199199
200−flag examples
201− description: Build including examples
202− manual: True
203− default: False
204−
205−flag examples-sdl
206− description: Build including SDL examples
207− manual: True
208− default: False
209−
210200flag use-c-malloc
211201 description: Use C malloc instead of GHC malloc
212202 manual: True
@@ -741,212 +731,3 @@ executable FileSystem.Event
741731 main-is: Streamly/Test/FileSystem/Event.hs
742732 if !flag(manual-tests)
743733 buildable: False
744−
745−-------------------------------------------------------------------------------
746−-- Examples
747−-------------------------------------------------------------------------------
748−
749−executable SearchQuery
750− import: exe-options
751− main-is: SearchQuery.hs
752− hs-source-dirs: examples
753− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
754− buildable: True
755− build-depends:
756− streamly
757− , base >= 4.9 && < 5
758− , http-conduit >= 2.2.2 && < 2.4
759− else
760− buildable: False
761−
762−executable ListDir
763− import: exe-options
764− main-is: ListDir.hs
765− hs-source-dirs: examples
766− if flag(examples) || flag(examples-sdl)
767− buildable: True
768− build-Depends:
769− streamly
770− , base >= 4.9 && < 5
771− else
772− buildable: False
773−
774−executable MergeSort
775− import: exe-options
776− main-is: MergeSort.hs
777− hs-source-dirs: examples
778− if flag(examples) || flag(examples-sdl)
779− buildable: True
780− build-Depends:
781− streamly
782− , base >= 4.9 && < 5
783− , random >= 1.0.0 && < 2
784− else
785− buildable: False
786−
787−executable AcidRain
788− import: exe-options
789− main-is: AcidRain.hs
790− hs-source-dirs: examples
791− if flag(examples) || flag(examples-sdl)
792− buildable: True
793− build-Depends:
794− streamly
795− , base >= 4.9 && < 5
796− , mtl >= 2.2 && < 3
797− else
798− buildable: False
799−
800−executable CirclingSquare
801− import: exe-options
802− main-is: CirclingSquare.hs
803− hs-source-dirs: examples
804− if flag(examples-sdl)
805− buildable: True
806− build-Depends:
807− streamly
808− , base >= 4.9 && < 5
809− , SDL >= 0.6.5 && < 0.7
810− else
811− buildable: False
812−
813−executable ControlFlow
814− import: exe-options
815− main-is: ControlFlow.hs
816− hs-source-dirs: examples
817− if flag(examples) || flag(examples-sdl)
818− buildable: True
819− build-Depends:
820− streamly
821− , base >= 4.9 && < 5
822− , exceptions >= 0.8 && < 0.11
823− , transformers >= 0.4 && < 0.6
824− , transformers-base >= 0.4 && < 0.5
825− else
826− buildable: False
827−
828−executable HandleIO
829− import: exe-options
830− main-is: HandleIO.hs
831− hs-source-dirs: examples
832− if flag(examples) || flag(examples-sdl)
833− buildable: True
834− build-Depends:
835− streamly
836− , base >= 4.9 && < 5
837− else
838− buildable: False
839−
840−executable FileIOExamples
841− import: exe-options
842− main-is: FileIOExamples.hs
843− hs-source-dirs: examples
844− if flag(examples) || flag(examples-sdl)
845− buildable: True
846− build-Depends:
847− streamly
848− , base >= 4.9 && < 5
849− else
850− buildable: False
851−
852−executable EchoServer
853− import: exe-options
854− main-is: EchoServer.hs
855− hs-source-dirs: examples
856− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
857− buildable: True
858− build-Depends:
859− streamly
860− , base >= 4.9 && < 5
861− , network >= 2.6 && < 4
862− else
863− buildable: False
864−
865−executable FileSinkServer
866− import: exe-options
867− main-is: FileSinkServer.hs
868− hs-source-dirs: examples
869− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
870− buildable: True
871− build-Depends:
872− streamly
873− , base >= 4.9 && < 5
874− , network >= 2.6 && < 4
875− else
876− buildable: False
877−
878−executable FromFileClient
879− import: exe-options
880− main-is: FromFileClient.hs
881− hs-source-dirs: examples
882− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
883− buildable: True
884− build-Depends:
885− streamly
886− , base >= 4.9 && < 5
887− else
888− buildable: False
889−
890−executable WordClassifier
891− import: exe-options
892− main-is: WordClassifier.hs
893− hs-source-dirs: examples
894− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
895− buildable: True
896− build-Depends:
897− streamly
898− , base >= 4.9 && < 5
899− , hashable >= 1.2 && < 1.4
900− , unordered-containers >= 0.2 && < 0.3
901− else
902− buildable: False
903−
904−executable WordCount
905− import: exe-options
906− main-is: WordCount.hs
907− hs-source-dirs: examples
908− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
909− buildable: True
910− build-Depends:
911− streamly
912− , base >= 4.9 && < 5
913− , vector >= 0.12 && < 0.13
914− else
915− buildable: False
916−
917−executable CamelCase
918− import: exe-options
919− main-is: CamelCase.hs
920− hs-source-dirs: examples
921− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
922− buildable: True
923− build-Depends:
924− streamly
925− , base >= 4.9 && < 5
926− else
927− buildable: False
928−
929−executable Rate
930− import: exe-options
931− main-is: Rate.hs
932− hs-source-dirs: examples
933− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
934− buildable: True
935− build-Depends:
936− streamly
937− , base >= 4.9 && < 5
938− else
939− buildable: False
940−
941−executable Split
942− import: exe-options
943− main-is: Split.hs
944− hs-source-dirs: examples
945− if (flag(examples) || flag(examples-sdl)) && !impl(ghcjs)
946− buildable: True
947− build-Depends:
948− streamly
949− , base >= 4.9 && < 5
950− , mtl >= 2.2 && < 3
951− else
952− buildable: False