on master help sign in
Move examples to streamly-examples repo.
d65f2b4pranaysashank
.travis.yml+6−6
@@ -115,9 +115,9 @@ matrix:
115 #- env: BUILD=cabal-v2 GHCVER=8.2.2 GHC_OPTIONS="" DISABLE_BENCH=y
115 #- env: BUILD=cabal-v2 GHCVER=8.2.2 GHC_OPTIONS="" DISABLE_BENCH=y
116 # addons: {apt: {packages: [cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
116 # addons: {apt: {packages: [cabal-install-2.4,ghc-8.2.2], sources: [hvr-ghc]}}
117
117
118− - name: "GHC 8.0.2+examples-sdl"
118+ - name: "GHC 8.0.2"
119− env: BUILD=cabal-v2 GHCVER=8.0.2 GHC_OPTIONS="" CABAL_BUILD_OPTIONS="--flags examples-sdl"
119+ env: BUILD=cabal-v2 GHCVER=8.0.2 GHC_OPTIONS=""
120− addons: {apt: {packages: [cabal-install-3.2,ghc-8.0.2,libsdl1.2-dev], sources: [hvr-ghc]}}
120+ addons: {apt: {packages: [cabal-install-3.2,ghc-8.0.2], sources: [hvr-ghc]}}
121 os: linux
121 os: linux
122 arch: amd64
122 arch: amd64
123
123
@@ -155,9 +155,9 @@ matrix:
155 #- env: BUILD=stack RESOLVER=nightly
155 #- env: BUILD=stack RESOLVER=nightly
156 # addons: {apt: {packages: [cabal-install-1.24], sources: [hvr-ghc]}}
156 # addons: {apt: {packages: [cabal-install-1.24], sources: [hvr-ghc]}}
157
157
158− - name: "GHC 8.8+stack lts-16.12+examples-sdl"
158+ - name: "GHC 8.8+stack lts-16.12"
159− env: BUILD=stack RESOLVER=lts-16.12 GHCVER=8.8 STACK_BUILD_OPTIONS="--flag streamly:examples-sdl" SDIST_OPTIONS="--ignore-check"
159+ env: BUILD=stack RESOLVER=lts-16.12 GHCVER=8.8 SDIST_OPTIONS="--ignore-check"
160− addons: {apt: {packages: [cabal-install-3.2,libsdl1.2-dev], sources: [hvr-ghc]}}
160+ addons: {apt: {packages: [cabal-install-3.2], sources: [hvr-ghc]}}
161 os: linux
161 os: linux
162 arch: amd64
162 arch: amd64
163
163
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
2
2
3−## Running directly using stack
3+Examples are available in [streamly-examples][https://github.com/composewell/streamly-examples] repo.
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−```
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 @@
24-- used to fine tune the concurrency control.
24-- used to fine tune the concurrency control.
25--
25--
26-- Streaming and concurrency together enable expressing reactive applications
26-- 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
28-- a simple SDL based FRP example. To summarize, streamly provides a unified
29-- a simple SDL based FRP example. To summarize, streamly provides a unified
29-- computing framework for streaming, non-determinism and functional reactive
30-- computing framework for streaming, non-determinism and functional reactive
30-- programming in an elegant and simple API that is a natural extension of pure
31-- 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))
912-- time. Take an example of a merge sort of two sorted streams. We need to
913-- time. Take an example of a merge sort of two sorted streams. We need to
913-- keep consuming items from the stream which has the lowest item in the sort
914-- keep consuming items from the stream which has the lowest item in the sort
914-- order. This can be achieved using async references to streams. See
915-- 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>.
916
917
917-- $monoid
918-- $monoid
918--
919--
@@ -1436,9 +1437,9 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1436-- and operators instead of the ugly pragmas.
1437-- and operators instead of the ugly pragmas.
1437--
1438--
1438-- For more concurrent programming examples see,
1439-- For more concurrent programming examples see,
1439−-- <examples/ListDir.hs ListDir.hs>,
1440+-- <https://github.com/composewell/streamly-examples/tree/master/ListDir.hs ListDir.hs>,
1440−-- <examples/MergeSort.hs MergeSort.hs> and
1441+-- <https://github.com/composewell/streamly-examples/tree/master/MergeSort.hs MergeSort.hs> and
1441−-- <examples/SearchQuery.hs SearchQuery.hs> in the examples directory.
1442+-- <https://github.com/composewell/streamly-examples/tree/master/SearchQuery.hs SearchQuery.hs> in the examples directory.
1442
1443
1443-- $reactive
1444-- $reactive
1444--
1445--
@@ -1512,14 +1513,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1512-- void $ runStateT runGame 60
1513-- void $ runStateT runGame 60
1513-- @
1514-- @
1514--
1515--
1515−-- You can also find the source of this example in the examples directory as
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>.
1516−-- <examples/AcidRain.hs AcidRain.hs>. It has been adapted from Gabriel's
1518+-- It has been adapted from Gabriel's
1517-- <https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html pipes-concurrency>
1519-- <https://hackage.haskell.org/package/pipes-concurrency-2.0.8/docs/Pipes-Concurrent-Tutorial.html pipes-concurrency>
1518-- package.
1520-- package.
1519-- This is much simpler compared to the pipes version because of the builtin
1521-- This is much simpler compared to the pipes version because of the builtin
1520-- concurrency in streamly. You can also find a SDL based reactive programming
1522-- concurrency in streamly. You can also find a SDL based reactive programming
1521-- example adapted from Yampa in
1523-- example adapted from Yampa in
1522−-- <examples/CirclingSquare.hs CirclingSquare.hs>.
1524+-- <https://github.com/composewell/streamly-examples/tree/master/CirclingSquare.hs CirclingSquare.hs>.
1523
1525
1524-- $performance
1526-- $performance
1525--
1527--
@@ -1714,15 +1716,15 @@ import Control.Monad.Trans.Class (MonadTrans (lift))
1714-- strong reactive programming library as well. Reactive programming is
1716-- strong reactive programming library as well. Reactive programming is
1715-- fundamentally stream of events that can be processed concurrently. The
1717-- fundamentally stream of events that can be processed concurrently. The
1716-- example in this tutorial as well as the
1718-- example in this tutorial as well as the
1717−-- <examples/CirclingSquare.hs CirclingSquare> example from Yampa demonstrate
1719+-- <https://github.com/composewell/streamly-examples/tree/master/CirclingSquare.hs CirclingSquare>
1718−-- the basic reactive capability of streamly. In core concepts streamly is
1720+-- example from Yampa demonstrate the basic reactive capability of streamly.
1719−-- strikingly similar to @dunai@. dunai was designed from a FRP perspective
1721+-- In core concepts streamly is strikingly similar to @dunai@. dunai was
1720−-- and streamly was originally designed from a concurrency perspective.
1722+-- designed from a FRP perspective and streamly was originally designed from a
1721−-- However, both have similarity at the core.
1723+-- concurrency perspective. However, both have similarity at the core.
1722
1724
1723-- $furtherReading
1725-- $furtherReading
1724--
1726--
1725-- * Read the documentation of "Streamly" module
1727-- * Read the documentation of "Streamly" module
1726-- * Read the documentation of "Streamly.Prelude" module
1728-- * 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.
1728-- * See the tests in the "test" directory of the package
1730-- * See the tests in the "test" directory of the package
streamly.cabal+1−220
@@ -68,7 +68,7 @@ description:
68 * /Interoperation/: "Streamly.Tutorial" module for interop with other
68 * /Interoperation/: "Streamly.Tutorial" module for interop with other
69 streaming libraries
69 streaming libraries
70 * /Reference Documentation/: Haddock documentation for the respective modules
70 * /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>
72 * /Guides/: <src/docs docs directory> in the package, for documentation on
72 * /Guides/: <src/docs docs directory> in the package, for documentation on
73 advanced topics, limitations, semantics of the library or on specific use
73 advanced topics, limitations, semantics of the library or on specific use
74 cases.
74 cases.
@@ -197,16 +197,6 @@ flag streamk
197 manual: True
197 manual: True
198 default: False
198 default: False
199
199
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−
210flag use-c-malloc
200flag use-c-malloc
211 description: Use C malloc instead of GHC malloc
201 description: Use C malloc instead of GHC malloc
212 manual: True
202 manual: True
@@ -741,212 +731,3 @@ executable FileSystem.Event
741 main-is: Streamly/Test/FileSystem/Event.hs
731 main-is: Streamly/Test/FileSystem/Event.hs
742 if !flag(manual-tests)
732 if !flag(manual-tests)
743 buildable: False
733 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