on master help sign in
Add concurrent lookahead stream type "Ahead"
9da3fccHarendra Kumar
benchmark/Linear.hs+2−1
@@ -52,9 +52,10 @@ main = do
52 , bgroup "append"
52 , bgroup "append"
53 [ benchIOAppend "serially" $ Ops.append serially
53 [ benchIOAppend "serially" $ Ops.append serially
54 , benchIOAppend "wSerially" $ Ops.append wSerially
54 , benchIOAppend "wSerially" $ Ops.append wSerially
55+ , benchIOAppend "aheadly" $ Ops.append aheadly
55 , benchIOAppend "asyncly" $ Ops.append asyncly
56 , benchIOAppend "asyncly" $ Ops.append asyncly
56 , benchIOAppend "wAsyncly" $ Ops.append wAsyncly
57 , benchIOAppend "wAsyncly" $ Ops.append wAsyncly
57− , benchIOAppend "parallely" $ Ops.append parallely
58+ , benchIOAppend "parallely" $ Ops.append parallely
58 ]
59 ]
59 , bgroup "compose"
60 , bgroup "compose"
60 [ benchIO "mapM" Ops.composeMapM
61 [ benchIO "mapM" Ops.composeMapM
benchmark/Nested.hs+13
@@ -53,6 +53,19 @@ main = do
53 , benchIO "breakAfterSome" $ Ops.breakAfterSome wSerially
53 , benchIO "breakAfterSome" $ Ops.breakAfterSome wSerially
54 ]
54 ]
55
55
56+ , bgroup "aheadly"
57+ [ benchIO "append" $ Ops.append aheadly
58+ , benchIO "toNull0" $ Ops.toNull0 aheadly
59+ , benchIO "toList0" $ Ops.toList0 aheadly
60+ , benchIO "toNull" $ Ops.toNull aheadly
61+ , benchIO "toList" $ Ops.toList aheadly
62+ , benchIO "toListSome" $ Ops.toListSome aheadly
63+ , benchIO "filterAllOut" $ Ops.filterAllOut aheadly
64+ , benchIO "filterAllIn" $ Ops.filterAllIn aheadly
65+ , benchIO "filterSome" $ Ops.filterSome aheadly
66+ , benchIO "breakAfterSome" $ Ops.breakAfterSome aheadly
67+ ]
68+
56 , bgroup "asyncly"
69 , bgroup "asyncly"
57 [ benchIO "append" $ Ops.append asyncly
70 [ benchIO "append" $ Ops.append asyncly
58 , benchIO "toNull0" $ Ops.toNull0 asyncly
71 , benchIO "toNull0" $ Ops.toNull0 asyncly
src/Streamly.hs+8−1
@@ -65,7 +65,11 @@ module Streamly
65 , SerialT
65 , SerialT
66 , WSerialT
66 , WSerialT
67
67
68− -- ** Parallel Streams
68+ -- ** Concurrent Lookahead Streams
69+ -- $lookahead
70+ , AheadT
71+
72+ -- ** Concurrent Asynchronous Streams
69 -- $async
73 -- $async
70 , AsyncT
74 , AsyncT
71 , WAsyncT
75 , WAsyncT
@@ -80,6 +84,7 @@ module Streamly
80 -- $sum
84 -- $sum
81 , serial
85 , serial
82 , wSerial
86 , wSerial
87+ , ahead
83 , async
88 , async
84 , wAsync
89 , wAsync
85 , parallel
90 , parallel
@@ -91,6 +96,7 @@ module Streamly
91 , serially
96 , serially
92 , wSerially
97 , wSerially
93 , asyncly
98 , asyncly
99+ , aheadly
94 , wAsyncly
100 , wAsyncly
95 , parallely
101 , parallely
96 , zipSerially
102 , zipSerially
@@ -100,6 +106,7 @@ module Streamly
100 -- * IO Streams
106 -- * IO Streams
101 , Serial
107 , Serial
102 , WSerial
108 , WSerial
109+ , Ahead
103 , Async
110 , Async
104 , WAsync
111 , WAsync
105 , Parallel
112 , Parallel
src/Streamly/Core.hs+330−2
@@ -36,6 +36,7 @@ module Streamly.Core
36 -- * Semigroup Style Composition
36 -- * Semigroup Style Composition
37 , serial
37 , serial
38 , wSerial
38 , wSerial
39+ , ahead
39 , async
40 , async
40 , wAsync
41 , wAsync
41 , parallel
42 , parallel
@@ -66,7 +67,7 @@ where
66
67
67import Control.Concurrent (ThreadId, myThreadId, threadDelay)
68import Control.Concurrent (ThreadId, myThreadId, threadDelay)
68import Control.Concurrent.MVar (MVar, newEmptyMVar, tryTakeMVar,
69import Control.Concurrent.MVar (MVar, newEmptyMVar, tryTakeMVar,
69− tryPutMVar, takeMVar)
70+ tryPutMVar, takeMVar, putMVar)
70import Control.Exception (SomeException (..))
71import Control.Exception (SomeException (..))
71import qualified Control.Exception.Lifted as EL
72import qualified Control.Exception.Lifted as EL
72import Control.Monad (when)
73import Control.Monad (when)
@@ -81,6 +82,8 @@ import Data.Atomics (casIORef, readForCAS, peekTicket
81import Data.Concurrent.Queue.MichaelScott (LinkedQueue, newQ, pushL,
82import Data.Concurrent.Queue.MichaelScott (LinkedQueue, newQ, pushL,
82 tryPopR, nullQ)
83 tryPopR, nullQ)
83import Data.Functor (void)
84import Data.Functor (void)
85+import Data.Heap (Heap, Entry(..))
86+import qualified Data.Heap as H
84import Data.IORef (IORef, modifyIORef, newIORef,
87import Data.IORef (IORef, modifyIORef, newIORef,
85 readIORef, atomicModifyIORef)
88 readIORef, atomicModifyIORef)
86import Data.Maybe (isNothing, fromJust)
89import Data.Maybe (isNothing, fromJust)
@@ -102,19 +105,27 @@ data ChildEvent a =
102 ChildYield a
105 ChildYield a
103 | ChildStop ThreadId (Maybe SomeException)
106 | ChildStop ThreadId (Maybe SomeException)
104
107
108+-- | Sorting out of turn outputs in a heap for Ahead style streams
109+data AheadHeapEntry a =
110+ AheadYieldSingle a
111+ | AheadYieldMany a (MVar ()) -- value, MVar to wakeup for more values
112+
105------------------------------------------------------------------------------
113------------------------------------------------------------------------------
106-- State threaded around the monad for thread management
114-- State threaded around the monad for thread management
107------------------------------------------------------------------------------
115------------------------------------------------------------------------------
108
116
117+-- XXX this is redundant as we always start a new SVar on bind
109-- | Conjunction is used for monadic/product style composition. Disjunction is
118-- | Conjunction is used for monadic/product style composition. Disjunction is
110-- used for fold/sum style composition. We need to distinguish the two types of
119-- used for fold/sum style composition. We need to distinguish the two types of
111-- SVars so that the scheduling of the two is independent.
120-- SVars so that the scheduling of the two is independent.
112data SVarTag = Conjunction | Disjunction deriving Eq
121data SVarTag = Conjunction | Disjunction deriving Eq
113
122
123+-- XXX use a separate data structure for each type of SVar
114data SVarSched =
124data SVarSched =
115 LIFO -- depth first concurrent
125 LIFO -- depth first concurrent
116 | FIFO -- breadth first concurrent
126 | FIFO -- breadth first concurrent
117 | Par -- all parallel
127 | Par -- all parallel
128+ | SVarAhead
118 deriving Eq
129 deriving Eq
119
130
120-- | Identify the type of the SVar. Two computations using the same style can
131-- | Identify the type of the SVar. Two computations using the same style can
@@ -155,7 +166,18 @@ data SVar m a =
155 , doorBell :: MVar () -- signal the consumer about output
166 , doorBell :: MVar () -- signal the consumer about output
156 , siren :: MVar () -- hooter for workers to begin work
167 , siren :: MVar () -- hooter for workers to begin work
157
168
169+ -- Output synchronization mechanism for Ahead streams (Ahead and
170+ -- wAhead). We maintain a heap of out of sequence ahead of time
171+ -- generated outputs and the sequence number of the task that is
172+ -- currently at the head of the stream. Concurrent execute ahead
173+ -- tasks that have a sequence number greater than the task at the
174+ -- head should add their output to the heap.
175+ , outputHeap :: IORef (Heap (Entry Int (AheadHeapEntry a)), Int)
176+ , heapSiren :: MVar () -- hooter for workers to continue
177+ -- adding to the heap
178+
158 -- Shared work queue
179 -- Shared work queue
180+ , workQueue :: IORef ([Stream m a], Int)
159 , enqueue :: Stream m a -> IO ()
181 , enqueue :: Stream m a -> IO ()
160 , queueEmpty :: m Bool
182 , queueEmpty :: m Bool
161 , runqueue :: m ()
183 , runqueue :: m ()
@@ -308,6 +330,9 @@ doFork action exHandler =
308
330
309-- TBD Each worker can have their own queue and the consumer can empty one
331-- TBD Each worker can have their own queue and the consumer can empty one
310-- queue at a time, that way contention can be reduced.
332-- queue at a time, that way contention can be reduced.
333+
334+-- | This function is used by the producer threads to queue output for the
335+-- consumer thread to consume.
311{-# INLINE send #-}
336{-# INLINE send #-}
312send :: SVar m a -> ChildEvent a -> IO ()
337send :: SVar m a -> ChildEvent a -> IO ()
313send sv msg = do
338send sv msg = do
@@ -340,6 +365,10 @@ send sv msg = do
340sendStop :: SVar m a -> IO ()
365sendStop :: SVar m a -> IO ()
341sendStop sv = myThreadId >>= \tid -> send sv (ChildStop tid Nothing)
366sendStop sv = myThreadId >>= \tid -> send sv (ChildStop tid Nothing)
342
367
368+-------------------------------------------------------------------------------
369+-- Async
370+-------------------------------------------------------------------------------
371+
343-- Note: For purely right associated expressions this queue should have at most
372-- Note: For purely right associated expressions this queue should have at most
344-- one element. It grows to more than one when we have left associcated
373-- one element. It grows to more than one when we have left associcated
345-- expressions. Large left associated compositions can grow this to a
374-- expressions. Large left associated compositions can grow this to a
@@ -367,6 +396,10 @@ runqueueLIFO sv q = run
367 [] -> ([], Nothing)
396 [] -> ([], Nothing)
368 x : xs -> (xs, Just x)
397 x : xs -> (xs, Just x)
369
398
399+-------------------------------------------------------------------------------
400+-- WAsync
401+-------------------------------------------------------------------------------
402+
370{-# INLINE enqueueFIFO #-}
403{-# INLINE enqueueFIFO #-}
371enqueueFIFO :: LinkedQueue (Stream m a) -> Stream m a -> IO ()
404enqueueFIFO :: LinkedQueue (Stream m a) -> Stream m a -> IO ()
372enqueueFIFO = pushL
405enqueueFIFO = pushL
@@ -387,6 +420,10 @@ runqueueFIFO sv q = run
387 single a = sendit a >> run
420 single a = sendit a >> run
388 yield a r = sendit a >> liftIO (enqueueFIFO q r) >> run
421 yield a r = sendit a >> liftIO (enqueueFIFO q r) >> run
389
422
423+-------------------------------------------------------------------------------
424+-- Parallel
425+-------------------------------------------------------------------------------
426+
390{-# INLINE runOne #-}
427{-# INLINE runOne #-}
391runOne :: MonadIO m => SVar m a -> Stream m a -> m ()
428runOne :: MonadIO m => SVar m a -> Stream m a -> m ()
392runOne sv m = (runStream m) (Just sv) stop single yield
429runOne sv m = (runStream m) (Just sv) stop single yield
@@ -398,6 +435,226 @@ runOne sv m = (runStream m) (Just sv) stop single yield
398 single a = sendit a >> stop
435 single a = sendit a >> stop
399 yield a r = sendit a >> runOne sv r
436 yield a r = sendit a >> runOne sv r
400
437
438+-------------------------------------------------------------------------------
439+-- Ahead
440+-------------------------------------------------------------------------------
441+
442+-- Lookahead streams can execute multiple tasks concurrently, ahead of time but
443+-- always serves them in the same order as they appear in the stream. To
444+-- implement lookahead streams efficiently we assign a sequence number to each
445+-- task when the task is picked up for execution. When the task finishes the
446+-- output is tagged with the same sequence number and we rearrange the outputs
447+-- in sequence based on that number.
448+--
449+-- To explain the mechanism imagine that the current task at the head of the
450+-- stream has a "token" to yield to the output. The ownership of the token is
451+-- determined by the current sequence number maintained in outputHeap. Sequence
452+-- number is assigned when a task is queued. When a thread dequeues a task it
453+-- picks up the sequence number as well and when the output is ready it uses
454+-- the sequence number to queue the output to the outputQueue.
455+--
456+-- The thread with current sequence number sends the output directly to the
457+-- outputQueue. Other threads push the output to the outputHeap. When the task
458+-- being queued on the heap is a stream of many elements we yield the first
459+-- element on the heap and an MVar to wakeup for more elements. When such a
460+-- task gets the "token" for outputQueue it can directly keep yielding all the
461+-- elements to the outputQueue without checking for the "token".
462+--
463+-- Note that no two outputs in the heap can have the same sequence numbers and
464+-- therefore we do not need a stable heap. We have also separated the buffer
465+-- for the current task (outputQueue) and the pending tasks (outputHeap) so
466+-- that the pending tasks cannot interfere with the current task. Note that for
467+-- a single task just the outputQueue is enough and for the case of many
468+-- threads just a heap is good enough. However we balance between these two
469+-- cases, so that both are efficient.
470+--
471+-- For bigger streams it may make sense to have separate buffers for each
472+-- stream. However, for singleton streams this may become inefficient. However,
473+-- if we do not have separate buffers, then the streams that come later in
474+-- sequence may hog the buffer, hindering the streams that are ahead. For this
475+-- reason we have a single element buffer limitation for the streams being
476+-- executed in advance.
477+--
478+-- This scheme works pretty efficiently with only 50% more overhead compared to
479+-- the Async streams where we do not have any kind of sequencing of the
480+-- outputs. It is especially devised so that we are most efficient when we have
481+-- short tasks and need just a single thread. Also when a thread yields many
482+-- items it can hold lockfree access to the outputQueue and do it efficiently.
483+--
484+-- XXX Maybe we can start the ahead threads at a lower cpu and IO priority so
485+-- that they do not hog the resources and hinder the progress of the threads in
486+-- front of them.
487+
488+{-# INLINE sendToHeap #-}
489+sendToHeap :: SVar m a -> AheadHeapEntry a -> Int -> IO Bool
490+sendToHeap sv msg seqNo = do
491+ let maxHeapSize = 1500
492+
493+ -- Maybe we can use a lock instead of using CAS
494+ -- insertions in the heap may be expensive if the heap is big
495+ -- Note: len is valid only when added is True
496+ (len, added) <- atomicModifyIORefCAS (outputHeap sv) $
497+ \ref@(h, snum) ->
498+ if seqNo > snum
499+ then ((H.insert (Entry seqNo msg) h, snum), (H.size h, True))
500+ else (ref, (0, False))
501+
502+ -- this is only for the case when the consumer generated siren can get
503+ -- missed due to race.
504+ when (len < maxHeapSize) $ void $ tryPutMVar (heapSiren sv) ()
505+
506+ if added
507+ then do
508+ when (len + 1 >= maxHeapSize) $ do
509+ takeMVar (heapSiren sv)
510+ (h, _) <- readIORef (outputHeap sv)
511+ when (H.size h <= maxHeapSize) $ void $ tryPutMVar (siren sv) ()
512+ return False
513+ else do
514+ case msg of
515+ AheadYieldSingle a -> send sv (ChildYield a)
516+ AheadYieldMany a mv -> do
517+ send sv (ChildYield a)
518+ putMVar mv ()
519+ return True
520+
521+popFromHeap
522+ :: IORef (Heap (Entry Int (AheadHeapEntry a)), Int)
523+ -> Int
524+ -> IO (Maybe (AheadHeapEntry a))
525+popFromHeap hp nextSeqNo =
526+ atomicModifyIORefCAS hp $ \(h, snum) -> do
527+ let r = H.uncons h
528+ let n = max nextSeqNo snum
529+ case r of
530+ Nothing -> ((h, n), Nothing)
531+ Just (Entry seqNo ev, hp') ->
532+ if (seqNo <= n)
533+ then ((hp', seqNo), Just ev)
534+ else ((h, n), Nothing)
535+
536+-- | As soon as we set the nextSeqNo in outputHeap we have transferred the
537+-- output "token" to the thread executing the task with that sequence number.
538+-- We have to make sure that there is no entry in the heap that has nextSeqNo
539+-- before we set nextSeqNo as the expected sequence number in the heap. It is
540+-- possible that when we are clearing the heap entry of the current sequence
541+-- number the next sequence number gets added and then we have to clear that,
542+-- this can keep going on and on.
543+drainHeap :: SVar m a -> Int -> IO ()
544+drainHeap sv nextSeqNo = do
545+ r <- popFromHeap (outputHeap sv) nextSeqNo
546+ case r of
547+ Nothing -> return ()
548+ Just ev -> do
549+ case ev of
550+ AheadYieldSingle a -> do
551+ send sv (ChildYield a)
552+ atomicModifyIORefCAS_ (outputHeap sv) $ \(h, snum) ->
553+ (h, snum + 1)
554+ drainHeap sv nextSeqNo
555+ AheadYieldMany a mv -> do
556+ send sv (ChildYield a)
557+ putMVar mv ()
558+
559+-- | The thread that has the token has the responsibility to move any pending
560+-- entries in the heap to the outputQueue. When the token is assigned to a
561+-- sequence number we must not have any entries for that sequence number in the
562+-- heap.
563+releaseToken :: SVar m a -> Int -> IO ()
564+releaseToken sv nextSeqNo = do
565+ drainHeap sv nextSeqNo
566+ void $ tryPutMVar (heapSiren sv) ()
567+
568+-- | When we have the output "token" we are allowed to append to the
569+-- outputQueue otherwise we have to insert the output in the outputHeap.
570+-- Returns whether we still have the token.
571+handleOutput :: SVar m a
572+ -> Bool
573+ -> Int
574+ -> Int
575+ -> a
576+ -> AheadHeapEntry a
577+ -> IO Bool
578+handleOutput sv haveToken prevSeqNo seqNo a ev = do
579+ if haveToken
580+ then do
581+ let nextSeqNo = prevSeqNo + 1
582+ if seqNo == nextSeqNo
583+ then send sv (ChildYield a) >> return True
584+ else do
585+ releaseToken sv nextSeqNo
586+ toHeap
587+ else toHeap
588+ where toHeap = sendToHeap sv ev seqNo
589+
590+-- Left associated ahead expressions are expensive. We start a new SVar for
591+-- each left associative expression. The queue is used only for right
592+-- associated expression, we queue the right expression and execute the left.
593+-- Thererefore the queue never has more than on item in it.
594+{-# INLINE enqueueAhead #-}
595+enqueueAhead :: IORef ([Stream m a], Int) -> Stream m a -> IO ()
596+enqueueAhead q m = do
597+ atomicModifyIORefCAS_ q $ \ case
598+ ([], n) -> ([m], n + 1) -- increment sequence
599+ _ -> error "not empty"
600+
601+runqueueAhead :: MonadIO m => SVar m a -> IORef ([Stream m a], Int) -> m ()
602+runqueueAhead sv q = run False (-1)
603+
604+ where
605+
606+ run haveToken prevSeqNo = do
607+ work <- dequeue
608+ case work of
609+ Nothing -> liftIO $ do
610+ when haveToken $ releaseToken sv (prevSeqNo + 1)
611+ sendStop sv
612+ Just (m, seqNo) -> do
613+ let tStatus = haveToken || seqNo == 0
614+ let stop = do
615+ let nextSeqNo = prevSeqNo + 1
616+ if tStatus && seqNo /= nextSeqNo
617+ then do
618+ liftIO $ releaseToken sv nextSeqNo
619+ run False seqNo
620+ else run tStatus seqNo
621+ let single a = do
622+ tokenStatus <- liftIO $
623+ handleOutput sv tStatus prevSeqNo seqNo a
624+ (AheadYieldSingle a)
625+ run tokenStatus seqNo
626+
627+ -- If we are yielding more than one item and we do not have the
628+ -- token then we insert the current item in the pending
629+ -- outputHeap and wait for our turn.
630+ let
631+ stopNext = run True seqNo
632+ singleNext a = do
633+ liftIO $ send sv (ChildYield a)
634+ run True seqNo
635+ yieldNext a r = do
636+ liftIO $ send sv (ChildYield a)
637+ (runStream r) (Just sv) stopNext singleNext yieldNext
638+
639+ yield a r = do
640+ -- XXX we can allocate the MVar when actually inserting
641+ -- in the heap and then return it.
642+ liftIO $ do
643+ mv <- newEmptyMVar
644+ gotToken <-
645+ handleOutput sv tStatus prevSeqNo seqNo a
646+ (AheadYieldMany a mv)
647+ when (not gotToken) $ void $ takeMVar mv
648+ (runStream r) (Just sv) stopNext singleNext yieldNext
649+
650+ (runStream m) (Just sv) stop single yield
651+
652+ dequeue = liftIO $ do
653+ atomicModifyIORefCAS q $ \case
654+ ([], n) -> (([], n), Nothing)
655+ (x : [], n) -> (([], n), Just (x, n))
656+ _ -> error "more than one item on queue"
657+
401-- Thread tracking is needed for two reasons:
658-- Thread tracking is needed for two reasons:
402--
659--
403-- 1) Killing threads on exceptions. Threads may not be left to go away by
660-- 1) Killing threads on exceptions. Threads may not be left to go away by
@@ -481,7 +738,9 @@ sendWorkerWait sv = do
481 (_, n) <- liftIO $ readIORef (outputQueue sv)
738 (_, n) <- liftIO $ readIORef (outputQueue sv)
482 when (n <= 0) $ do
739 when (n <= 0) $ do
483 -- XXX the queue may be empty temporarily if the worker has dequeued
740 -- XXX the queue may be empty temporarily if the worker has dequeued
484− -- the work item but has not enqueued the remaining part yet.
741+ -- the work item but has not enqueued the remaining part yet. For the
742+ -- same reason, a worker may come back if it tries to dequeue when the
743+ -- queue is empty, even though the whole work has not finished yet.
485 done <- queueEmpty sv
744 done <- queueEmpty sv
486 if not done
745 if not done
487 then do
746 then do
@@ -553,7 +812,10 @@ getFifoSVar ctype = do
553 SVar { outputQueue = outQ
812 SVar { outputQueue = outQ
554 , doorBell = outQMv
813 , doorBell = outQMv
555 , siren = hooter
814 , siren = hooter
815+ , outputHeap = undefined
816+ , heapSiren = undefined
556 , runningThreads = running
817 , runningThreads = running
818+ , workQueue = undefined
557 , runqueue = runqueueFIFO sv q
819 , runqueue = runqueueFIFO sv q
558 , enqueue = pushL q
820 , enqueue = pushL q
559 , queueEmpty = liftIO $ nullQ q
821 , queueEmpty = liftIO $ nullQ q
@@ -575,7 +837,10 @@ getLifoSVar ctype = do
575 SVar { outputQueue = outQ
837 SVar { outputQueue = outQ
576 , doorBell = outQMv
838 , doorBell = outQMv
577 , siren = hooter
839 , siren = hooter
840+ , outputHeap = undefined
841+ , heapSiren = undefined
578 , runningThreads = running
842 , runningThreads = running
843+ , workQueue = undefined
579 , runqueue = runqueueLIFO sv q
844 , runqueue = runqueueLIFO sv q
580 , enqueue = enqueueLIFO q
845 , enqueue = enqueueLIFO q
581 , queueEmpty = checkEmpty
846 , queueEmpty = checkEmpty
@@ -595,7 +860,10 @@ getParSVar style = do
595 SVar { outputQueue = outQ
860 SVar { outputQueue = outQ
596 , doorBell = outQMv
861 , doorBell = outQMv
597 , siren = hooter
862 , siren = hooter
863+ , outputHeap = undefined
864+ , heapSiren = undefined
598 , runningThreads = running
865 , runningThreads = running
866+ , workQueue = undefined
599 , runqueue = undefined
867 , runqueue = undefined
600 , enqueue = undefined
868 , enqueue = undefined
601 , queueEmpty = undefined
869 , queueEmpty = undefined
@@ -604,6 +872,35 @@ getParSVar style = do
604 }
872 }
605 in return sv
873 in return sv
606
874
875+getAheadSVar :: MonadIO m => SVarStyle -> IO (SVar m a)
876+getAheadSVar style = do
877+ outQ <- newIORef ([], 0)
878+ outH <- newIORef (H.empty, 0)
879+ outQMv <- newEmptyMVar
880+ hooter <- newEmptyMVar
881+ hsiren <- newEmptyMVar
882+ active <- newIORef 0
883+ running <- newIORef S.empty
884+ q <- newIORef ([], -1)
885+ let checkEmpty = liftIO $ do
886+ (xs, _) <- readIORef q
887+ return $ null xs
888+ let sv =
889+ SVar { outputQueue = outQ
890+ , doorBell = outQMv
891+ , siren = hooter
892+ , outputHeap = outH
893+ , heapSiren = hsiren
894+ , runningThreads = running
895+ , workQueue = q
896+ , runqueue = runqueueAhead sv q
897+ , enqueue = undefined
898+ , queueEmpty = checkEmpty
899+ , svarStyle = style
900+ , activeWorkers = active
901+ }
902+ in return sv
903+
607-- | Create a new empty SVar.
904-- | Create a new empty SVar.
608newEmptySVar :: MonadAsync m => SVarStyle -> m (SVar m a)
905newEmptySVar :: MonadAsync m => SVarStyle -> m (SVar m a)
609newEmptySVar style = do
906newEmptySVar style = do
@@ -612,6 +909,7 @@ newEmptySVar style = do
612 SVarStyle _ FIFO -> getFifoSVar style
909 SVarStyle _ FIFO -> getFifoSVar style
613 SVarStyle _ LIFO -> getLifoSVar style
910 SVarStyle _ LIFO -> getLifoSVar style
614 SVarStyle _ Par -> getParSVar style
911 SVarStyle _ Par -> getParSVar style
912+ SVarStyle _ SVarAhead -> getAheadSVar style
615
913
616-- | Create a new SVar and enqueue one stream computation on it.
914-- | Create a new SVar and enqueue one stream computation on it.
617newStreamVar1 :: MonadAsync m => SVarStyle -> Stream m a -> m (SVar m a)
915newStreamVar1 :: MonadAsync m => SVarStyle -> Stream m a -> m (SVar m a)
@@ -624,6 +922,17 @@ newStreamVar1 style m = do
624 pushWorker sv
922 pushWorker sv
625 return sv
923 return sv
626
924
925+-- | Create a new SVar and enqueue one stream computation on it.
926+newStreamVarAhead :: MonadAsync m => Stream m a -> m (SVar m a)
927+newStreamVarAhead m = do
928+ sv <- newEmptySVar (SVarStyle Disjunction SVarAhead)
929+ -- Note: We must have all the work on the queue before sending the
930+ -- pushworker, otherwise the pushworker may exit before we even get a
931+ -- chance to push.
932+ liftIO $ enqueueAhead (workQueue sv) m
933+ pushWorker sv
934+ return sv
935+
627-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
936-- | Write a stream to an 'SVar' in a non-blocking manner. The stream can then
628-- be read back from the SVar using 'fromSVar'.
937-- be read back from the SVar using 'fromSVar'.
629toStreamVar :: MonadAsync m => SVar m a -> Stream m a -> m ()
938toStreamVar :: MonadAsync m => SVar m a -> Stream m a -> m ()
@@ -727,6 +1036,25 @@ joinStreamVarPar style m1 m2 = Stream $ \svr stp sng yld ->
727 pushWorkerPar sv m2
1036 pushWorkerPar sv m2
728 (runStream (fromStreamVar sv)) Nothing stp sng yld
1037 (runStream (fromStreamVar sv)) Nothing stp sng yld
729
1038
1039+{-# INLINE ahead #-}
1040+ahead :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1041+ahead m1 m2 = Stream $ \svr stp sng yld -> do
1042+ let style = SVarStyle Disjunction SVarAhead
1043+ case svr of
1044+ Just sv | svarStyle sv == style -> do
1045+ liftIO $ enqueueAhead (workQueue sv) m2
1046+ -- Always run the left side on a new SVar to avoid complexity in
1047+ -- sequencing results. This means the left side cannot further
1048+ -- split into more ahead computations on the same SVar.
1049+ (runStream m1) Nothing stp sng yld
1050+ _ -> do
1051+ sv <- newStreamVarAhead (concurrently m1 m2)
1052+ (runStream (fromStreamVar sv)) Nothing stp sng yld
1053+ where
1054+ concurrently ma mb = Stream $ \svr stp sng yld -> do
1055+ liftIO $ enqueueAhead (workQueue (fromJust svr)) mb
1056+ (runStream ma) Nothing stp sng yld
1057+
730------------------------------------------------------------------------------
1058------------------------------------------------------------------------------
731-- Semigroup and Monoid style compositions for parallel actions
1059-- Semigroup and Monoid style compositions for parallel actions
732------------------------------------------------------------------------------
1060------------------------------------------------------------------------------
src/Streamly/Streams.hs+139−6
@@ -53,6 +53,7 @@ module Streamly.Streams
53 -- * Merging Streams
53 -- * Merging Streams
54 , serial
54 , serial
55 , wSerial
55 , wSerial
56+ , ahead
56 , async
57 , async
57 , wAsync
58 , wAsync
58 , parallel
59 , parallel
@@ -62,6 +63,7 @@ module Streamly.Streams
62 -- * IO Streams
63 -- * IO Streams
63 , Serial
64 , Serial
64 , WSerial
65 , WSerial
66+ , Ahead
65 , Async
67 , Async
66 , WAsync
68 , WAsync
67 , Parallel
69 , Parallel
@@ -73,6 +75,7 @@ module Streamly.Streams
73 , StreamT -- deprecated
75 , StreamT -- deprecated
74 , WSerialT
76 , WSerialT
75 , InterleavedT -- deprecated
77 , InterleavedT -- deprecated
78+ , AheadT
76 , AsyncT
79 , AsyncT
77 , WAsyncT
80 , WAsyncT
78 , ParallelT
81 , ParallelT
@@ -84,6 +87,7 @@ module Streamly.Streams
84 , serially -- deprecated
87 , serially -- deprecated
85 , wSerially
88 , wSerially
86 , interleaving -- deprecated
89 , interleaving -- deprecated
90+ , aheadly
87 , asyncly
91 , asyncly
88 , wAsyncly
92 , wAsyncly
89 , parallely
93 , parallely
@@ -349,6 +353,9 @@ instance (MonadState s m CONSTRAINT) => MonadState s (STREAM m) where { \
349-- all elements from the second stream.
353-- all elements from the second stream.
350--
354--
351-- @
355-- @
356+-- import Streamly
357+-- import qualified "Streamly.Prelude" as S
358+--
352-- main = ('toList' . 'serially' $ (fromFoldable [1,2]) \<\> (fromFoldable [3,4])) >>= print
359-- main = ('toList' . 'serially' $ (fromFoldable [1,2]) \<\> (fromFoldable [3,4])) >>= print
353-- @
360-- @
354-- @
361-- @
@@ -361,7 +368,7 @@ instance (MonadState s m CONSTRAINT) => MonadState s (STREAM m) where { \
361-- @
368-- @
362-- main = 'runStream' . 'serially' $ do
369-- main = 'runStream' . 'serially' $ do
363-- x <- return 1 \<\> return 2
370-- x <- return 1 \<\> return 2
364−-- liftIO $ print x
371+-- S.once $ print x
365-- @
372-- @
366-- @
373-- @
367-- 1
374-- 1
@@ -374,7 +381,7 @@ instance (MonadState s m CONSTRAINT) => MonadState s (STREAM m) where { \
374-- main = 'runStream' . 'serially' $ do
381-- main = 'runStream' . 'serially' $ do
375-- x <- return 1 \<\> return 2
382-- x <- return 1 \<\> return 2
376-- y <- return 3 \<\> return 4
383-- y <- return 3 \<\> return 4
377−-- liftIO $ print (x, y)
384+-- S.once $ print (x, y)
378-- @
385-- @
379-- @
386-- @
380-- (1,3)
387-- (1,3)
@@ -450,6 +457,9 @@ MONAD_COMMON_INSTANCES(SerialT,)
450-- two streams, yielding one element from each stream alternately.
457-- two streams, yielding one element from each stream alternately.
451--
458--
452-- @
459-- @
460+-- import Streamly
461+-- import qualified "Streamly.Prelude" as S
462+--
453-- main = ('toList' . 'wSerially' $ (fromFoldable [1,2]) \<\> (fromFoldable [3,4])) >>= print
463-- main = ('toList' . 'wSerially' $ (fromFoldable [1,2]) \<\> (fromFoldable [3,4])) >>= print
454-- @
464-- @
455-- @
465-- @
@@ -464,7 +474,7 @@ MONAD_COMMON_INSTANCES(SerialT,)
464-- main = 'runStream' . 'wSerially' $ do
474-- main = 'runStream' . 'wSerially' $ do
465-- x <- return 1 \<\> return 2
475-- x <- return 1 \<\> return 2
466-- y <- return 3 \<\> return 4
476-- y <- return 3 \<\> return 4
467−-- liftIO $ print (x, y)
477+-- S.once $ print (x, y)
468-- @
478-- @
469-- @
479-- @
470-- (1,3)
480-- (1,3)
@@ -542,6 +552,114 @@ instance Monad m => Monad (WSerialT m) where
542MONAD_APPLICATIVE_INSTANCE(WSerialT,)
552MONAD_APPLICATIVE_INSTANCE(WSerialT,)
543MONAD_COMMON_INSTANCES(WSerialT,)
553MONAD_COMMON_INSTANCES(WSerialT,)
544
554
555+------------------------------------------------------------------------------
556+-- AheadT
557+------------------------------------------------------------------------------
558+
559+-- | Deep ahead composition or ahead composition with depth first traversal.
560+-- The semigroup composition of 'AheadT' appends streams in a depth first
561+-- manner just like 'SerialT' except that it can produce elements concurrently
562+-- ahead of time. It is like 'AsyncT' except that 'AsyncT' produces the output
563+-- as it arrives whereas 'AheadT' orders the output in the traversal order.
564+--
565+-- @
566+-- main = ('toList' . 'aheadly' $ (fromFoldable [1,2]) \<> (fromFoldable [3,4])) >>= print
567+-- @
568+-- @
569+-- [1,2,3,4]
570+-- @
571+--
572+-- Any exceptions generated by a constituent stream are propagated to the
573+-- output stream.
574+--
575+-- Similarly, the monad instance of 'AheadT' may run each iteration
576+-- concurrently ahead of time but presents the results in the same order as
577+-- 'SerialT'.
578+--
579+-- @
580+-- import "Streamly"
581+-- import qualified "Streamly.Prelude" as S
582+-- import Control.Concurrent
583+--
584+-- main = 'runStream' . 'aheadly' $ do
585+-- n <- return 3 \<\> return 2 \<\> return 1
586+-- S.once $ do
587+-- threadDelay (n * 1000000)
588+-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
589+-- @
590+-- @
591+-- ThreadId 40: Delay 1
592+-- ThreadId 39: Delay 2
593+-- ThreadId 38: Delay 3
594+-- @
595+--
596+-- All iterations may run in the same thread if they do not block.
597+--
598+-- Note that ahead composition with depth first traversal can be used to
599+-- combine infinite number of streams as it explores only a bounded number of
600+-- streams at a time.
601+--
602+-- @since 0.3.0
603+newtype AheadT m a = AheadT {getAheadT :: S.Stream m a}
604+ deriving (Functor, MonadTrans)
605+
606+instance IsStream AheadT where
607+ toStream = getAheadT
608+ fromStream = AheadT
609+
610+------------------------------------------------------------------------------
611+-- Semigroup
612+------------------------------------------------------------------------------
613+
614+-- | Polymorphic version of the 'Semigroup' operation '<>' of 'AheadT'.
615+-- Merges two streams sequentially but with concurrent lookahead.
616+--
617+-- @since 0.3.0
618+{-# INLINE ahead #-}
619+ahead :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
620+ahead m1 m2 = fromStream $ S.ahead (toStream m1) (toStream m2)
621+
622+instance MonadAsync m => Semigroup (AheadT m a) where
623+ (<>) = ahead
624+
625+------------------------------------------------------------------------------
626+-- Monoid
627+------------------------------------------------------------------------------
628+
629+instance MonadAsync m => Monoid (AheadT m a) where
630+ mempty = nil
631+ mappend = (<>)
632+
633+------------------------------------------------------------------------------
634+-- Monad
635+------------------------------------------------------------------------------
636+
637+{-# INLINE aheadbind #-}
638+aheadbind
639+ :: MonadAsync m
640+ => S.Stream m a
641+ -> (a -> S.Stream m b)
642+ -> S.Stream m b
643+aheadbind m f = go m
644+ where
645+ go (S.Stream g) =
646+ S.Stream $ \ctx stp sng yld ->
647+ let run x = (S.runStream x) ctx stp sng yld
648+ single a = run $ f a
649+ yield a r = run $ f a `S.ahead` go r
650+ in g Nothing stp single yield
651+
652+instance MonadAsync m => Monad (AheadT m) where
653+ return = pure
654+ (AheadT m) >>= f = AheadT $ aheadbind m (getAheadT . f)
655+
656+------------------------------------------------------------------------------
657+-- Other instances
658+------------------------------------------------------------------------------
659+
660+MONAD_APPLICATIVE_INSTANCE(AheadT,MONADPARALLEL)
661+MONAD_COMMON_INSTANCES(AheadT, MONADPARALLEL)
662+
545------------------------------------------------------------------------------
663------------------------------------------------------------------------------
546-- AsyncT
664-- AsyncT
547------------------------------------------------------------------------------
665------------------------------------------------------------------------------
@@ -573,11 +691,12 @@ MONAD_COMMON_INSTANCES(WSerialT,)
573--
691--
574-- @
692-- @
575-- import "Streamly"
693-- import "Streamly"
694+-- import qualified "Streamly.Prelude" as S
576-- import Control.Concurrent
695-- import Control.Concurrent
577--
696--
578-- main = 'runStream' . 'asyncly' $ do
697-- main = 'runStream' . 'asyncly' $ do
579-- n <- return 3 \<\> return 2 \<\> return 1
698-- n <- return 3 \<\> return 2 \<\> return 1
580−-- liftIO $ do
699+-- S.once $ do
581-- threadDelay (n * 1000000)
700-- threadDelay (n * 1000000)
582-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
701-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
583-- @
702-- @
@@ -691,11 +810,12 @@ MONAD_COMMON_INSTANCES(AsyncT, MONADPARALLEL)
691--
810--
692-- @
811-- @
693-- import "Streamly"
812-- import "Streamly"
813+-- import qualified "Streamly.Prelude" as S
694-- import Control.Concurrent
814-- import Control.Concurrent
695--
815--
696-- main = 'runStream' . 'wAsyncly' $ do
816-- main = 'runStream' . 'wAsyncly' $ do
697-- n <- return 3 \<\> return 2 \<\> return 1
817-- n <- return 3 \<\> return 2 \<\> return 1
698−-- liftIO $ do
818+-- S.once $ do
699-- threadDelay (n * 1000000)
819-- threadDelay (n * 1000000)
700-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
820-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
701-- @
821-- @
@@ -829,11 +949,12 @@ MONAD_COMMON_INSTANCES(WAsyncT, MONADPARALLEL)
829--
949--
830-- @
950-- @
831-- import "Streamly"
951-- import "Streamly"
952+-- import qualified "Streamly.Prelude" as S
832-- import Control.Concurrent
953-- import Control.Concurrent
833--
954--
834-- main = 'runStream' . 'parallely' $ do
955-- main = 'runStream' . 'parallely' $ do
835-- n <- return 3 \<\> return 2 \<\> return 1
956-- n <- return 3 \<\> return 2 \<\> return 1
836−-- liftIO $ do
957+-- S.once $ do
837-- threadDelay (n * 1000000)
958-- threadDelay (n * 1000000)
838-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
959-- myThreadId >>= \\tid -> putStrLn (show tid ++ ": Delay " ++ show n)
839-- @
960-- @
@@ -991,6 +1112,12 @@ wSerially = adapt
991interleaving :: IsStream t => WSerialT m a -> t m a
1112interleaving :: IsStream t => WSerialT m a -> t m a
992interleaving = wSerially
1113interleaving = wSerially
993
1114
1115+-- | Fix the type of a polymorphic stream as 'AheadT'.
1116+--
1117+-- @since 0.3.0
1118+aheadly :: IsStream t => AheadT m a -> t m a
1119+aheadly = adapt
1120+
994-- | Fix the type of a polymorphic stream as 'AsyncT'.
1121-- | Fix the type of a polymorphic stream as 'AsyncT'.
995--
1122--
996-- @since 0.1.0
1123-- @since 0.1.0
@@ -1097,6 +1224,12 @@ type Serial a = SerialT IO a
1097-- @since 0.2.0
1224-- @since 0.2.0
1098type WSerial a = WSerialT IO a
1225type WSerial a = WSerialT IO a
1099
1226
1227+-- | A serial IO stream of elements of type @a@ with concurrent lookahead. See
1228+-- 'AheadT' documentation for more details.
1229+--
1230+-- @since 0.3.0
1231+type Ahead a = AheadT IO a
1232+
1100-- | A demand driven left biased parallely composing IO stream of elements of
1233-- | A demand driven left biased parallely composing IO stream of elements of
1101-- type @a@. See 'AsyncT' documentation for more details.
1234-- type @a@. See 'AsyncT' documentation for more details.
1102--
1235--
streamly.cabal+4−2
@@ -119,12 +119,14 @@ library
119 build-depends: base >= 4.8 && < 5
119 build-depends: base >= 4.8 && < 5
120 , atomic-primops >= 0.8 && < 0.9
120 , atomic-primops >= 0.8 && < 0.9
121 , containers >= 0.5 && < 0.6
121 , containers >= 0.5 && < 0.6
122+ , heaps >= 0.3 && < 0.4
123+ , lockfree-queue >= 0.2.3 && < 0.3
124+
125+ -- transfomers
122 , exceptions >= 0.8 && < 0.11
126 , exceptions >= 0.8 && < 0.11
123 , lifted-base >= 0.2 && < 0.3
127 , lifted-base >= 0.2 && < 0.3
124− , lockfree-queue >= 0.2.3 && < 0.3
125 , monad-control >= 1.0 && < 2
128 , monad-control >= 1.0 && < 2
126 , mtl >= 2.2 && < 3
129 , mtl >= 2.2 && < 3
127− , stm >= 2.4.3 && < 2.5
128 , transformers >= 0.4 && < 0.6
130 , transformers >= 0.4 && < 0.6
129 , transformers-base >= 0.4 && < 0.5
131 , transformers-base >= 0.4 && < 0.5
130
132
test/Main.hs+98−2
@@ -115,6 +115,7 @@ main = hspec $ do
115 ---------------------------------------------------------------------------
115 ---------------------------------------------------------------------------
116
116
117 describe "Serial Composition" $ compose serially mempty id
117 describe "Serial Composition" $ compose serially mempty id
118+ describe "Ahead Composition" $ compose aheadly mempty id
118 describe "WSerial Composition" $ compose wSerially mempty sort
119 describe "WSerial Composition" $ compose wSerially mempty sort
119 describe "Async Composition" $ compose asyncly mempty sort
120 describe "Async Composition" $ compose asyncly mempty sort
120 describe "WAsync Composition" $ compose wAsyncly mempty sort
121 describe "WAsync Composition" $ compose wAsyncly mempty sort
@@ -128,7 +129,10 @@ main = hspec $ do
128 ---------------------------------------------------------------------------
129 ---------------------------------------------------------------------------
129
130
130 -- TBD need more such combinations to be tested.
131 -- TBD need more such combinations to be tested.
132+ describe "serial <> and serial <>" $ composeAndComposeSimple serially serially (cycle [[1 .. 9]])
133+ describe "ahead <> and ahead <>" $ composeAndComposeSimple aheadly aheadly (cycle [[1 .. 9]])
131− describe "<> and <>" $ composeAndComposeSimple serially serially (cycle [[1 .. 9]])
134+ describe "ahead <> and serial <>" $ composeAndComposeSimple aheadly serially (cycle [[1 .. 9]])
135+ describe "serial <> and ahead <>" $ composeAndComposeSimple serially aheadly (cycle [[1 .. 9]])
132
136
133 describe "<> and <=>" $ composeAndComposeSimple
137 describe "<> and <=>" $ composeAndComposeSimple
134 serially
138 serially
@@ -203,6 +207,7 @@ main = hspec $ do
203 ---------------------------------------------------------------------------
207 ---------------------------------------------------------------------------
204
208
205 describe "Serial loops" $ loops serially id reverse
209 describe "Serial loops" $ loops serially id reverse
210+ describe "Ahead loops" $ loops aheadly id reverse
206 describe "Async parallel loops" $ loops asyncly sort sort
211 describe "Async parallel loops" $ loops asyncly sort sort
207 describe "WAsync loops" $ loops wAsyncly sort sort
212 describe "WAsync loops" $ loops wAsyncly sort sort
208 describe "parallel loops" $ loops parallely sort sort
213 describe "parallel loops" $ loops parallely sort sort
@@ -216,30 +221,42 @@ main = hspec $ do
216 describe "Bind and compose Stream 3" $ bindAndComposeSimple serially asyncly
221 describe "Bind and compose Stream 3" $ bindAndComposeSimple serially asyncly
217 describe "Bind and compose Stream 4" $ bindAndComposeSimple serially wAsyncly
222 describe "Bind and compose Stream 4" $ bindAndComposeSimple serially wAsyncly
218 describe "Bind and compose Stream 5" $ bindAndComposeSimple serially parallely
223 describe "Bind and compose Stream 5" $ bindAndComposeSimple serially parallely
224+ describe "Bind and compose Stream 6" $ bindAndComposeSimple serially aheadly
225+
226+ describe "Bind and compose Ahead Stream 0" $ bindAndComposeSimple aheadly aheadly
227+ describe "Bind and compose Ahead Stream 1" $ bindAndComposeSimple aheadly serially
228+ describe "Bind and compose Ahead Stream 2" $ bindAndComposeSimple aheadly wSerially
229+ describe "Bind and compose Ahead Stream 3" $ bindAndComposeSimple aheadly asyncly
230+ describe "Bind and compose Ahead Stream 4" $ bindAndComposeSimple aheadly wAsyncly
231+ describe "Bind and compose Ahead Stream 5" $ bindAndComposeSimple aheadly parallely
219
232
220 describe "Bind and compose Costream 1" $ bindAndComposeSimple wSerially serially
233 describe "Bind and compose Costream 1" $ bindAndComposeSimple wSerially serially
221 describe "Bind and compose Costream 2" $ bindAndComposeSimple wSerially wSerially
234 describe "Bind and compose Costream 2" $ bindAndComposeSimple wSerially wSerially
222 describe "Bind and compose Costream 3" $ bindAndComposeSimple wSerially asyncly
235 describe "Bind and compose Costream 3" $ bindAndComposeSimple wSerially asyncly
223 describe "Bind and compose Costream 4" $ bindAndComposeSimple wSerially wAsyncly
236 describe "Bind and compose Costream 4" $ bindAndComposeSimple wSerially wAsyncly
224 describe "Bind and compose Costream 5" $ bindAndComposeSimple wSerially parallely
237 describe "Bind and compose Costream 5" $ bindAndComposeSimple wSerially parallely
238+ describe "Bind and compose Costream 6" $ bindAndComposeSimple wSerially aheadly
225
239
226 describe "Bind and compose Async 1" $ bindAndComposeSimple asyncly serially
240 describe "Bind and compose Async 1" $ bindAndComposeSimple asyncly serially
227 describe "Bind and compose Async 2" $ bindAndComposeSimple asyncly wSerially
241 describe "Bind and compose Async 2" $ bindAndComposeSimple asyncly wSerially
228 describe "Bind and compose Async 3" $ bindAndComposeSimple asyncly asyncly
242 describe "Bind and compose Async 3" $ bindAndComposeSimple asyncly asyncly
229 describe "Bind and compose Async 4" $ bindAndComposeSimple asyncly wAsyncly
243 describe "Bind and compose Async 4" $ bindAndComposeSimple asyncly wAsyncly
230 describe "Bind and compose Async 5" $ bindAndComposeSimple asyncly parallely
244 describe "Bind and compose Async 5" $ bindAndComposeSimple asyncly parallely
245+ describe "Bind and compose Async 6" $ bindAndComposeSimple asyncly aheadly
231
246
232 describe "Bind and compose WAsync 1" $ bindAndComposeSimple wAsyncly serially
247 describe "Bind and compose WAsync 1" $ bindAndComposeSimple wAsyncly serially
233 describe "Bind and compose WAsync 2" $ bindAndComposeSimple wAsyncly wSerially
248 describe "Bind and compose WAsync 2" $ bindAndComposeSimple wAsyncly wSerially
234 describe "Bind and compose WAsync 3" $ bindAndComposeSimple wAsyncly asyncly
249 describe "Bind and compose WAsync 3" $ bindAndComposeSimple wAsyncly asyncly
235 describe "Bind and compose WAsync 4" $ bindAndComposeSimple wAsyncly wAsyncly
250 describe "Bind and compose WAsync 4" $ bindAndComposeSimple wAsyncly wAsyncly
236 describe "Bind and compose WAsync 5" $ bindAndComposeSimple wAsyncly parallely
251 describe "Bind and compose WAsync 5" $ bindAndComposeSimple wAsyncly parallely
252+ describe "Bind and compose WAsync 6" $ bindAndComposeSimple wAsyncly aheadly
237
253
238 describe "Bind and compose Parallel 1" $ bindAndComposeSimple parallely serially
254 describe "Bind and compose Parallel 1" $ bindAndComposeSimple parallely serially
239 describe "Bind and compose Parallel 2" $ bindAndComposeSimple parallely wSerially
255 describe "Bind and compose Parallel 2" $ bindAndComposeSimple parallely wSerially
240 describe "Bind and compose Parallel 3" $ bindAndComposeSimple parallely asyncly
256 describe "Bind and compose Parallel 3" $ bindAndComposeSimple parallely asyncly
241 describe "Bind and compose Parallel 4" $ bindAndComposeSimple parallely wAsyncly
257 describe "Bind and compose Parallel 4" $ bindAndComposeSimple parallely wAsyncly
242 describe "Bind and compose Parallel 5" $ bindAndComposeSimple parallely parallely
258 describe "Bind and compose Parallel 5" $ bindAndComposeSimple parallely parallely
259+ describe "Bind and compose Parallel 6" $ bindAndComposeSimple parallely aheadly
243
260
244 let fldr, fldl :: (IsStream t, Semigroup (t IO Int)) => [t IO Int] -> t IO Int
261 let fldr, fldl :: (IsStream t, Semigroup (t IO Int)) => [t IO Int] -> t IO Int
245 fldr = foldr (<>) nil
262 fldr = foldr (<>) nil
@@ -255,6 +272,21 @@ main = hspec $ do
255 describe "Bind and compose" $ bindAndComposeHierarchy serially wAsyncly k
272 describe "Bind and compose" $ bindAndComposeHierarchy serially wAsyncly k
256 forM_ [fldr, fldl] $ \k ->
273 forM_ [fldr, fldl] $ \k ->
257 describe "Bind and compose" $ bindAndComposeHierarchy serially parallely k
274 describe "Bind and compose" $ bindAndComposeHierarchy serially parallely k
275+ forM_ [fldr, fldl] $ \k ->
276+ describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy serially aheadly k
277+
278+ forM_ [fldr, fldl] $ \k ->
279+ describe "Bind and compose aheadly serially" $ bindAndComposeHierarchy aheadly serially k
280+ forM_ [fldr, fldl] $ \k ->
281+ describe "Bind and compose aheadly wSerially" $ bindAndComposeHierarchy aheadly wSerially k
282+ forM_ [fldr, fldl] $ \k ->
283+ describe "Bind and compose aheadly asyncly" $ bindAndComposeHierarchy aheadly asyncly k
284+ forM_ [fldr, fldl] $ \k ->
285+ describe "Bind and compose aheadly wAsyncly" $ bindAndComposeHierarchy aheadly wAsyncly k
286+ forM_ [fldr, fldl] $ \k ->
287+ describe "Bind and compose aheadly parallely" $ bindAndComposeHierarchy aheadly parallely k
288+ forM_ [fldr, fldl] $ \k ->
289+ describe "Bind and compose serially aheadly" $ bindAndComposeHierarchy aheadly aheadly k
258
290
259 forM_ [fldr, fldl] $ \k ->
291 forM_ [fldr, fldl] $ \k ->
260 describe "Bind and compose" $ bindAndComposeHierarchy wSerially serially k
292 describe "Bind and compose" $ bindAndComposeHierarchy wSerially serially k
@@ -266,6 +298,8 @@ main = hspec $ do
266 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wAsyncly k
298 describe "Bind and compose" $ bindAndComposeHierarchy wSerially wAsyncly k
267 forM_ [fldr, fldl] $ \k ->
299 forM_ [fldr, fldl] $ \k ->
268 describe "Bind and compose" $ bindAndComposeHierarchy wSerially parallely k
300 describe "Bind and compose" $ bindAndComposeHierarchy wSerially parallely k
301+ forM_ [fldr, fldl] $ \k ->
302+ describe "Bind and compose wserially aheadly" $ bindAndComposeHierarchy wSerially aheadly k
269
303
270 forM_ [fldr, fldl] $ \k ->
304 forM_ [fldr, fldl] $ \k ->
271 describe "Bind and compose" $ bindAndComposeHierarchy asyncly serially k
305 describe "Bind and compose" $ bindAndComposeHierarchy asyncly serially k
@@ -277,6 +311,8 @@ main = hspec $ do
277 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wAsyncly k
311 describe "Bind and compose" $ bindAndComposeHierarchy asyncly wAsyncly k
278 forM_ [fldr, fldl] $ \k ->
312 forM_ [fldr, fldl] $ \k ->
279 describe "Bind and compose" $ bindAndComposeHierarchy asyncly parallely k
313 describe "Bind and compose" $ bindAndComposeHierarchy asyncly parallely k
314+ forM_ [fldr, fldl] $ \k ->
315+ describe "Bind and compose asyncly aheadly" $ bindAndComposeHierarchy asyncly aheadly k
280
316
281 forM_ [fldr, fldl] $ \k ->
317 forM_ [fldr, fldl] $ \k ->
282 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly serially k
318 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly serially k
@@ -288,6 +324,8 @@ main = hspec $ do
288 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wAsyncly k
324 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly wAsyncly k
289 forM_ [fldr, fldl] $ \k ->
325 forM_ [fldr, fldl] $ \k ->
290 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly parallely k
326 describe "Bind and compose" $ bindAndComposeHierarchy wAsyncly parallely k
327+ forM_ [fldr, fldl] $ \k ->
328+ describe "Bind and compose wAsyncly aheadly" $ bindAndComposeHierarchy wAsyncly aheadly k
291
329
292 forM_ [fldr, fldl] $ \k ->
330 forM_ [fldr, fldl] $ \k ->
293 describe "Bind and compose" $ bindAndComposeHierarchy parallely serially k
331 describe "Bind and compose" $ bindAndComposeHierarchy parallely serially k
@@ -299,15 +337,19 @@ main = hspec $ do
299 describe "Bind and compose" $ bindAndComposeHierarchy parallely wAsyncly k
337 describe "Bind and compose" $ bindAndComposeHierarchy parallely wAsyncly k
300 forM_ [fldr, fldl] $ \k ->
338 forM_ [fldr, fldl] $ \k ->
301 describe "Bind and compose" $ bindAndComposeHierarchy parallely parallely k
339 describe "Bind and compose" $ bindAndComposeHierarchy parallely parallely k
340+ forM_ [fldr, fldl] $ \k ->
341+ describe "Bind and compose parallely aheadly" $ bindAndComposeHierarchy parallely aheadly k
302
342
303 -- Nest two lists using different styles of product compositions
343 -- Nest two lists using different styles of product compositions
304 it "Nests two streams using monadic serial composition" nestTwoSerial
344 it "Nests two streams using monadic serial composition" nestTwoSerial
345+ it "Nests two streams using monadic ahead composition" nestTwoAhead
305 it "Nests two streams using monadic interleaved composition" nestTwoInterleaved
346 it "Nests two streams using monadic interleaved composition" nestTwoInterleaved
306 it "Nests two streams using monadic Async composition" nestTwoAsync
347 it "Nests two streams using monadic Async composition" nestTwoAsync
307 it "Nests two streams using monadic WAsync composition" nestTwoWAsync
348 it "Nests two streams using monadic WAsync composition" nestTwoWAsync
308 it "Nests two streams using monadic parallel composition" nestTwoParallel
349 it "Nests two streams using monadic parallel composition" nestTwoParallel
309
350
310 it "Nests two streams using applicative serial composition" nestTwoSerialApp
351 it "Nests two streams using applicative serial composition" nestTwoSerialApp
352+ it "Nests two streams using applicative ahead composition" nestTwoAheadApp
311 it "Nests two streams using applicative interleaved composition" nestTwoInterleavedApp
353 it "Nests two streams using applicative interleaved composition" nestTwoInterleavedApp
312 it "Nests two streams using applicative Async composition" nestTwoAsyncApp
354 it "Nests two streams using applicative Async composition" nestTwoAsyncApp
313 it "Nests two streams using applicative WAsync composition" nestTwoWAsyncApp
355 it "Nests two streams using applicative WAsync composition" nestTwoWAsyncApp
@@ -319,6 +361,7 @@ main = hspec $ do
319
361
320 -- TBD combine all binds and all compose in one example
362 -- TBD combine all binds and all compose in one example
321 describe "Miscellaneous combined examples" mixedOps
363 describe "Miscellaneous combined examples" mixedOps
364+ describe "Miscellaneous combined examples aheadly" mixedOpsAheadly
322 describe "Simple MonadError and MonadThrow" simpleMonadError
365 describe "Simple MonadError and MonadThrow" simpleMonadError
323
366
324 {-
367 {-
@@ -333,12 +376,18 @@ main = hspec $ do
333 describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
376 describe "Composed MonadThrow asyncly" $ composeWithMonadThrow asyncly
334 describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
377 describe "Composed MonadThrow wAsyncly" $ composeWithMonadThrow wAsyncly
335 describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
378 describe "Composed MonadThrow parallely" $ composeWithMonadThrow parallely
379+ describe "Composed MonadThrow aheadly" $ composeWithMonadThrow aheadly
336
380
337− it "Crosses thread limit (2000 threads)" $
381+ it "asyncly crosses thread limit (2000 threads)" $
338 runStream (asyncly $ fold $
382 runStream (asyncly $ fold $
339 replicate 2000 $ A.once $ threadDelay 1000000)
383 replicate 2000 $ A.once $ threadDelay 1000000)
340 `shouldReturn` ()
384 `shouldReturn` ()
341
385
386+ it "aheadly crosses thread limit (4000 threads)" $
387+ runStream (aheadly $ fold $
388+ replicate 4000 $ A.once $ threadDelay 1000000)
389+ `shouldReturn` ()
390+
342-- XXX need to test that we have promptly cleaned up everything after the error
391-- XXX need to test that we have promptly cleaned up everything after the error
343-- XXX We can also check the output that we are expected to get before the
392-- XXX We can also check the output that we are expected to get before the
344-- error occurs.
393-- error occurs.
@@ -432,6 +481,16 @@ nestTwoSerial =
432 return (x + y)
481 return (x + y)
433 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
482 ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
434
483
484+nestTwoAhead :: Expectation
485+nestTwoAhead =
486+ let s1 = foldMapWith (<>) return [1..4]
487+ s2 = foldMapWith (<>) return [5..8]
488+ in (A.toList . aheadly) (do
489+ x <- s1
490+ y <- s2
491+ return (x + y)
492+ ) `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
493+
435nestTwoSerialApp :: Expectation
494nestTwoSerialApp :: Expectation
436nestTwoSerialApp =
495nestTwoSerialApp =
437 let s1 = foldMapWith (<>) return [1..4]
496 let s1 = foldMapWith (<>) return [1..4]
@@ -439,6 +498,13 @@ nestTwoSerialApp =
439 in toListSerial ((+) <$> s1 <*> s2)
498 in toListSerial ((+) <$> s1 <*> s2)
440 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
499 `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
441
500
501+nestTwoAheadApp :: Expectation
502+nestTwoAheadApp =
503+ let s1 = foldMapWith (<>) return [1..4]
504+ s2 = foldMapWith (<>) return [5..8]
505+ in (A.toList . aheadly) ((+) <$> s1 <*> s2)
506+ `shouldReturn` ([6,7,8,9,7,8,9,10,8,9,10,11,9,10,11,12] :: [Int])
507+
442nestTwoInterleaved :: Expectation
508nestTwoInterleaved :: Expectation
443nestTwoInterleaved =
509nestTwoInterleaved =
444 let s1 = foldMapWith (<>) return [1..4]
510 let s1 = foldMapWith (<>) return [1..4]
@@ -706,3 +772,33 @@ mixedOps = do
706 return (x11 + y11 + z11)
772 return (x11 + y11 + z11)
707 return (x1 + y1 + z1)
773 return (x1 + y1 + z1)
708 return (x + y + z)
774 return (x + y + z)
775+
776+mixedOpsAheadly :: Spec
777+mixedOpsAheadly = do
778+ it "Compose many ops" $
779+ (toListSerial composeMixed >>= return . sort)
780+ `shouldReturn` ([8,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,11,11
781+ ,11,11,11,11,11,11,11,11,12,12,12,12,12,13
782+ ] :: [Int])
783+ where
784+
785+ composeMixed :: SerialT IO Int
786+ composeMixed = do
787+ A.once $ return ()
788+ A.once $ putStr ""
789+ x <- return 1
790+ y <- return 2
791+ z <- do
792+ x1 <- wAsyncly $ return 1 <> return 2
793+ A.once $ return ()
794+ A.once $ putStr ""
795+ y1 <- aheadly $ return 1 <> return 2
796+ z1 <- do
797+ x11 <- return 1 <> return 2
798+ y11 <- aheadly $ return 1 <> return 2
799+ z11 <- parallely $ return 1 <> return 2
800+ A.once $ return ()
801+ A.once $ putStr ""
802+ return (x11 + y11 + z11)
803+ return (x1 + y1 + z1)
804+ return (x + y + z)
test/Prop.hs+19−1
@@ -318,6 +318,8 @@ main = hspec $ do
318 functorOps folded "serially folded" serially (==)
318 functorOps folded "serially folded" serially (==)
319 functorOps A.fromFoldable "wSerially" wSerially (==)
319 functorOps A.fromFoldable "wSerially" wSerially (==)
320 functorOps folded "wSerially folded" wSerially (==)
320 functorOps folded "wSerially folded" wSerially (==)
321+ functorOps A.fromFoldable "aheadly" aheadly (==)
322+ functorOps folded "aheadly folded" aheadly (==)
321 functorOps A.fromFoldable "asyncly" asyncly sortEq
323 functorOps A.fromFoldable "asyncly" asyncly sortEq
322 functorOps folded "asyncly folded" asyncly sortEq
324 functorOps folded "asyncly folded" asyncly sortEq
323 functorOps A.fromFoldable "wAsyncly" wAsyncly sortEq
325 functorOps A.fromFoldable "wAsyncly" wAsyncly sortEq
@@ -332,6 +334,7 @@ main = hspec $ do
332 describe "Semigroup operations" $ do
334 describe "Semigroup operations" $ do
333 semigroupOps "serially" serially (==)
335 semigroupOps "serially" serially (==)
334 semigroupOps "wSerially" wSerially (==)
336 semigroupOps "wSerially" wSerially (==)
337+ semigroupOps "aheadly" aheadly (==)
335 semigroupOps "asyncly" asyncly sortEq
338 semigroupOps "asyncly" asyncly sortEq
336 semigroupOps "wAsyncly" wAsyncly sortEq
339 semigroupOps "wAsyncly" wAsyncly sortEq
337 semigroupOps "parallely" parallely sortEq
340 semigroupOps "parallely" parallely sortEq
@@ -344,6 +347,8 @@ main = hspec $ do
344 -- XXX applicative with three arguments
347 -- XXX applicative with three arguments
345 prop "serially applicative" $ applicativeOps A.fromFoldable serially (==)
348 prop "serially applicative" $ applicativeOps A.fromFoldable serially (==)
346 prop "serially applicative folded" $ applicativeOps folded serially (==)
349 prop "serially applicative folded" $ applicativeOps folded serially (==)
350+ prop "aheadly applicative" $ applicativeOps A.fromFoldable aheadly (==)
351+ prop "aheadly applicative folded" $ applicativeOps folded aheadly (==)
347 prop "wSerially applicative" $ applicativeOps A.fromFoldable wSerially sortEq
352 prop "wSerially applicative" $ applicativeOps A.fromFoldable wSerially sortEq
348 prop "wSerially applicative folded" $ applicativeOps folded wSerially sortEq
353 prop "wSerially applicative folded" $ applicativeOps folded wSerially sortEq
349 prop "asyncly applicative" $ applicativeOps A.fromFoldable asyncly sortEq
354 prop "asyncly applicative" $ applicativeOps A.fromFoldable asyncly sortEq
@@ -357,6 +362,8 @@ main = hspec $ do
357 -- prop "zipAsyncly applicative" $ zipApplicative zipAsyncly (==)
362 -- prop "zipAsyncly applicative" $ zipApplicative zipAsyncly (==)
358 prop "zip monadic serially" $ zipMonadic A.fromFoldable serially (==)
363 prop "zip monadic serially" $ zipMonadic A.fromFoldable serially (==)
359 prop "zip monadic serially folded" $ zipMonadic folded serially (==)
364 prop "zip monadic serially folded" $ zipMonadic folded serially (==)
365+ prop "zip monadic aheadly" $ zipMonadic A.fromFoldable aheadly (==)
366+ prop "zip monadic aheadly folded" $ zipMonadic folded aheadly (==)
360 prop "zip monadic wSerially" $ zipMonadic A.fromFoldable wSerially (==)
367 prop "zip monadic wSerially" $ zipMonadic A.fromFoldable wSerially (==)
361 prop "zip monadic wSerially folded" $ zipMonadic folded wSerially (==)
368 prop "zip monadic wSerially folded" $ zipMonadic folded wSerially (==)
362 prop "zip monadic asyncly" $ zipMonadic A.fromFoldable asyncly (==)
369 prop "zip monadic asyncly" $ zipMonadic A.fromFoldable asyncly (==)
@@ -368,18 +375,21 @@ main = hspec $ do
368
375
369 describe "Monad operations" $ do
376 describe "Monad operations" $ do
370 prop "serially monad then" $ monadThen A.fromFoldable serially (==)
377 prop "serially monad then" $ monadThen A.fromFoldable serially (==)
378+ prop "aheadly monad then" $ monadThen A.fromFoldable aheadly (==)
371 prop "wSerially monad then" $ monadThen A.fromFoldable wSerially sortEq
379 prop "wSerially monad then" $ monadThen A.fromFoldable wSerially sortEq
372 prop "asyncly monad then" $ monadThen A.fromFoldable asyncly sortEq
380 prop "asyncly monad then" $ monadThen A.fromFoldable asyncly sortEq
373 prop "wAsyncly monad then" $ monadThen A.fromFoldable wAsyncly sortEq
381 prop "wAsyncly monad then" $ monadThen A.fromFoldable wAsyncly sortEq
374 prop "parallely monad then" $ monadThen A.fromFoldable parallely sortEq
382 prop "parallely monad then" $ monadThen A.fromFoldable parallely sortEq
375
383
376 prop "serially monad then folded" $ monadThen folded serially (==)
384 prop "serially monad then folded" $ monadThen folded serially (==)
385+ prop "aheadly monad then folded" $ monadThen folded aheadly (==)
377 prop "wSerially monad then folded" $ monadThen folded wSerially sortEq
386 prop "wSerially monad then folded" $ monadThen folded wSerially sortEq
378 prop "asyncly monad then folded" $ monadThen folded asyncly sortEq
387 prop "asyncly monad then folded" $ monadThen folded asyncly sortEq
379 prop "wAsyncly monad then folded" $ monadThen folded wAsyncly sortEq
388 prop "wAsyncly monad then folded" $ monadThen folded wAsyncly sortEq
380 prop "parallely monad then folded" $ monadThen folded parallely sortEq
389 prop "parallely monad then folded" $ monadThen folded parallely sortEq
381
390
382 prop "serially monad bind" $ monadBind A.fromFoldable serially (==)
391 prop "serially monad bind" $ monadBind A.fromFoldable serially (==)
392+ prop "aheadly monad bind" $ monadBind A.fromFoldable aheadly (==)
383 prop "wSerially monad bind" $ monadBind A.fromFoldable wSerially sortEq
393 prop "wSerially monad bind" $ monadBind A.fromFoldable wSerially sortEq
384 prop "asyncly monad bind" $ monadBind A.fromFoldable asyncly sortEq
394 prop "asyncly monad bind" $ monadBind A.fromFoldable asyncly sortEq
385 prop "wAsyncly monad bind" $ monadBind A.fromFoldable wAsyncly sortEq
395 prop "wAsyncly monad bind" $ monadBind A.fromFoldable wAsyncly sortEq
@@ -387,6 +397,7 @@ main = hspec $ do
387
397
388 describe "Stream transform operations" $ do
398 describe "Stream transform operations" $ do
389 transformOps A.fromFoldable "serially" serially (==)
399 transformOps A.fromFoldable "serially" serially (==)
400+ transformOps A.fromFoldable "aheadly" aheadly (==)
390 transformOps A.fromFoldable "wSerially" wSerially (==)
401 transformOps A.fromFoldable "wSerially" wSerially (==)
391 transformOps A.fromFoldable "zipSerially" zipSerially (==)
402 transformOps A.fromFoldable "zipSerially" zipSerially (==)
392 transformOps A.fromFoldable "zipAsyncly" zipAsyncly (==)
403 transformOps A.fromFoldable "zipAsyncly" zipAsyncly (==)
@@ -395,6 +406,7 @@ main = hspec $ do
395 transformOps A.fromFoldable "parallely" parallely sortEq
406 transformOps A.fromFoldable "parallely" parallely sortEq
396
407
397 transformOps folded "serially folded" serially (==)
408 transformOps folded "serially folded" serially (==)
409+ transformOps folded "aheadly folded" aheadly (==)
398 transformOps folded "wSerially folded" wSerially (==)
410 transformOps folded "wSerially folded" wSerially (==)
399 transformOps folded "zipSerially folded" zipSerially (==)
411 transformOps folded "zipSerially folded" zipSerially (==)
400 transformOps folded "zipAsyncly folded" zipAsyncly (==)
412 transformOps folded "zipAsyncly folded" zipAsyncly (==)
@@ -403,6 +415,7 @@ main = hspec $ do
403 transformOps folded "parallely folded" parallely sortEq
415 transformOps folded "parallely folded" parallely sortEq
404
416
405 transformOpsWord8 A.fromFoldable "serially" serially
417 transformOpsWord8 A.fromFoldable "serially" serially
418+ transformOpsWord8 A.fromFoldable "aheadly" aheadly
406 transformOpsWord8 A.fromFoldable "wSerially" wSerially
419 transformOpsWord8 A.fromFoldable "wSerially" wSerially
407 transformOpsWord8 A.fromFoldable "zipSerially" zipSerially
420 transformOpsWord8 A.fromFoldable "zipSerially" zipSerially
408 transformOpsWord8 A.fromFoldable "zipAsyncly" zipAsyncly
421 transformOpsWord8 A.fromFoldable "zipAsyncly" zipAsyncly
@@ -411,6 +424,7 @@ main = hspec $ do
411 transformOpsWord8 A.fromFoldable "parallely" parallely
424 transformOpsWord8 A.fromFoldable "parallely" parallely
412
425
413 transformOpsWord8 folded "serially folded" serially
426 transformOpsWord8 folded "serially folded" serially
427+ transformOpsWord8 folded "aheadly folded" aheadly
414 transformOpsWord8 folded "wSerially folded" wSerially
428 transformOpsWord8 folded "wSerially folded" wSerially
415 transformOpsWord8 folded "zipSerially folded" zipSerially
429 transformOpsWord8 folded "zipSerially folded" zipSerially
416 transformOpsWord8 folded "zipAsyncly folded" zipAsyncly
430 transformOpsWord8 folded "zipAsyncly folded" zipAsyncly
@@ -420,6 +434,7 @@ main = hspec $ do
420
434
421 describe "Stream elimination operations" $ do
435 describe "Stream elimination operations" $ do
422 eliminationOps A.fromFoldable "serially" serially
436 eliminationOps A.fromFoldable "serially" serially
437+ eliminationOps A.fromFoldable "aheadly" aheadly
423 eliminationOps A.fromFoldable "wSerially" wSerially
438 eliminationOps A.fromFoldable "wSerially" wSerially
424 eliminationOps A.fromFoldable "zipSerially" zipSerially
439 eliminationOps A.fromFoldable "zipSerially" zipSerially
425 eliminationOps A.fromFoldable "zipAsyncly" zipAsyncly
440 eliminationOps A.fromFoldable "zipAsyncly" zipAsyncly
@@ -428,6 +443,7 @@ main = hspec $ do
428 eliminationOps A.fromFoldable "parallely" parallely
443 eliminationOps A.fromFoldable "parallely" parallely
429
444
430 eliminationOps folded "serially folded" serially
445 eliminationOps folded "serially folded" serially
446+ eliminationOps folded "aheadly folded" aheadly
431 eliminationOps folded "wSerially folded" wSerially
447 eliminationOps folded "wSerially folded" wSerially
432 eliminationOps folded "zipSerially folded" zipSerially
448 eliminationOps folded "zipSerially folded" zipSerially
433 eliminationOps folded "zipAsyncly folded" zipAsyncly
449 eliminationOps folded "zipAsyncly folded" zipAsyncly
@@ -435,13 +451,15 @@ main = hspec $ do
435 eliminationOps folded "wAsyncly folded" wAsyncly
451 eliminationOps folded "wAsyncly folded" wAsyncly
436 eliminationOps folded "parallely folded" parallely
452 eliminationOps folded "parallely folded" parallely
437
453
438− describe "Stream elimination operations" $ do
454+ describe "Stream serial elimination operations" $ do
439 serialEliminationOps A.fromFoldable "serially" serially
455 serialEliminationOps A.fromFoldable "serially" serially
456+ serialEliminationOps A.fromFoldable "aheadly" aheadly
440 serialEliminationOps A.fromFoldable "wSerially" wSerially
457 serialEliminationOps A.fromFoldable "wSerially" wSerially
441 serialEliminationOps A.fromFoldable "zipSerially" zipSerially
458 serialEliminationOps A.fromFoldable "zipSerially" zipSerially
442 serialEliminationOps A.fromFoldable "zipAsyncly" zipAsyncly
459 serialEliminationOps A.fromFoldable "zipAsyncly" zipAsyncly
443
460
444 serialEliminationOps folded "serially folded" serially
461 serialEliminationOps folded "serially folded" serially
462+ serialEliminationOps folded "aheadly folded" aheadly
445 serialEliminationOps folded "wSerially folded" wSerially
463 serialEliminationOps folded "wSerially folded" wSerially
446 serialEliminationOps folded "zipSerially folded" zipSerially
464 serialEliminationOps folded "zipSerially folded" zipSerially
447 serialEliminationOps folded "zipAsyncly folded" zipAsyncly
465 serialEliminationOps folded "zipAsyncly folded" zipAsyncly