on master help sign in
Move APIs conflicting with Prelude to Prelude module
3d17104Harendra Kumar
asyncly.cabal+1
@@ -43,6 +43,7 @@ library
43 hs-source-dirs: src
43 hs-source-dirs: src
44 exposed-modules: Asyncly
44 exposed-modules: Asyncly
45 , Asyncly.AsyncT
45 , Asyncly.AsyncT
46+ , Asyncly.Prelude
46 , Asyncly.Time
47 , Asyncly.Time
47
48
48 default-language: Haskell2010
49 default-language: Haskell2010
benchmark/Main.hs+1
@@ -8,6 +8,7 @@ import Control.Monad (guard)
8import Criterion.Main (defaultMain, bgroup, bench, nfIO)
8import Criterion.Main (defaultMain, bgroup, bench, nfIO)
9
9
10import qualified Asyncly as A
10import qualified Asyncly as A
11+import qualified Asyncly.Prelude as A
11
12
12#ifdef EXTRA_BENCHMARKS
13#ifdef EXTRA_BENCHMARKS
13import Control.Monad.IO.Class (MonadIO (liftIO))
14import Control.Monad.IO.Class (MonadIO (liftIO))
src/Asyncly.hs−15
@@ -126,9 +126,7 @@ module Asyncly
126 -- * Running
126 -- * Running
127 , runAsyncly
127 , runAsyncly
128 , toList
128 , toList
129− , uncons
130 , async
129 , async
131− , unfoldr
132
130
133 -- * Monadic Composition (Conjunction)
131 -- * Monadic Composition (Conjunction)
134 -- $bind
132 -- $bind
@@ -146,18 +144,6 @@ module Asyncly
146 , foldMapWith
144 , foldMapWith
147 , forEachWith
145 , forEachWith
148
146
149− -- * Special folds
150− , take
151− , drop
152−
153− -- * Zipping
154− , zipWith
155− , zipWithM
156− , ZipSerial (..)
157− , zipAsyncWithM
158− , zipAsyncWith
159− , ZipAsync (..)
160−
161 -- * Re-exports
147 -- * Re-exports
162 , Monoid (..)
148 , Monoid (..)
163 , Semigroup (..)
149 , Semigroup (..)
@@ -174,7 +160,6 @@ import Control.Applicative (Alternative(..))
174import Control.Monad (MonadPlus(..))
160import Control.Monad (MonadPlus(..))
175import Control.Monad.IO.Class (MonadIO (..))
161import Control.Monad.IO.Class (MonadIO (..))
176import Control.Monad.Trans.Class (MonadTrans (..))
162import Control.Monad.Trans.Class (MonadTrans (..))
177−import Prelude hiding (take, drop, zipWith)
178
163
179-- $monoidal
164-- $monoidal
180--
165--
src/Asyncly/AsyncT.hs+37−169
@@ -1,8 +1,6 @@
1{-# LANGUAGE ConstraintKinds #-}
1{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE EmptyCase #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving#-}
6{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE UndecidableInstances #-} -- XXX
6{-# LANGUAGE UndecidableInstances #-} -- XXX
@@ -22,19 +20,7 @@ module Asyncly.AsyncT
22 , MonadAsync
20 , MonadAsync
23 , runAsyncly
21 , runAsyncly
24 , toList
22 , toList
25− , uncons
26 , async
23 , async
27− , unfoldr
28−
29− , take
30− , drop
31−
32− , zipWithM
33− , zipWith
34− , ZipSerial (..)
35− , zipAsyncWithM
36− , zipAsyncWith
37− , ZipAsync (..)
38
24
39 , interleave
25 , interleave
40 , (<=>)
26 , (<=>)
@@ -79,10 +65,9 @@ import Data.IORef (IORef, modifyIORef, newIORef,
79import Data.Atomics (atomicModifyIORefCAS,
65import Data.Atomics (atomicModifyIORefCAS,
80 atomicModifyIORefCAS_)
66 atomicModifyIORefCAS_)
81import Data.Maybe (isNothing)
67import Data.Maybe (isNothing)
82−import Data.Semigroup (Semigroup(..), cycle1)
68+import Data.Semigroup (Semigroup(..))
83import Data.Set (Set)
69import Data.Set (Set)
84import qualified Data.Set as S
70import qualified Data.Set as S
85−import Prelude hiding (take, drop, zipWith)
86
71
87------------------------------------------------------------------------------
72------------------------------------------------------------------------------
88-- Concurrency Semantics
73-- Concurrency Semantics
@@ -591,6 +576,42 @@ instance MonadAsync m => MonadPlus (AsyncT m) where
591 mzero = empty
576 mzero = empty
592 mplus = (<|>)
577 mplus = (<|>)
593
578
579+-- | The computation specified in the argument is pushed to a new thread and
580+-- the context is computation pulls output from the thread.
581+pushOneToCtx :: MonadAsync m => CtxType -> AsyncT m a -> m (Context m a)
582+pushOneToCtx ctype m = do
583+ ctx <- liftIO $
584+ case ctype of
585+ CtxType _ FIFO -> do
586+ c <- getFifoCtx ctype
587+ return c
588+ CtxType _ LIFO -> do
589+ c <- getLifoCtx ctype
590+ return c
591+ -- Note: We must have all the work on the queue before sending the
592+ -- pushworker, otherwise the pushworker may exit before we even get a
593+ -- chance to push.
594+ liftIO $ (enqueue ctx) m
595+ pushWorker ctx
596+ return ctx
597+
598+-- XXX The async API is useful for exploring each stream arbitrarily when
599+-- zipping or merging two streams. We can use a newtype wrapper with a monad
600+-- instance that composes like regular streaming libraries to facilitate linear
601+-- composition. We will also need a yield API for that.
602+
603+-- | Run a computation asynchronously, triggers the computation and returns
604+-- another computation (i.e. a promise) that when executed produces the output
605+-- from the original computation. Note that the returned action must be
606+-- executed exactly once and drained completely. If not executed or not drained
607+-- fully we will may have a thread blocked forever and if executed more than
608+-- once a ContextUsedAfterEOF exception will be raised.
609+
610+async :: MonadAsync m => AsyncT m a -> m (AsyncT m a)
611+async m = do
612+ ctx <- pushOneToCtx (CtxType Disjunction LIFO) m
613+ return $ pullFromCtx ctx
614+
594------------------------------------------------------------------------------
615------------------------------------------------------------------------------
595-- Num
616-- Num
596------------------------------------------------------------------------------
617------------------------------------------------------------------------------
@@ -707,159 +728,6 @@ toList m = (runAsyncT m) Nothing stop yield
707 yield a Nothing = return [a]
728 yield a Nothing = return [a]
708 yield a (Just x) = liftM (a :) (toList x)
729 yield a (Just x) = liftM (a :) (toList x)
709
730
710−-- | Decompose a stream into its head and tail. If the stream is empty, returns
711−-- 'Nothing'. If the stream is non-empty, returns 'Just (a, ma)', where 'a' is
712−-- the head of the stream and 'ma' its tail.
713−uncons :: MonadAsync m => AsyncT m a -> m (Maybe (a, AsyncT m a))
714−uncons m = (runAsyncT m) Nothing stop yield
715−
716− where
717−
718− stop = return Nothing
719−
720− {-# INLINE yield #-}
721− yield a Nothing = return (Just (a, empty))
722− yield a (Just x) = return (Just (a, x))
723−
724−-- | Build a Stream by unfolding steps starting from a seed.
725−unfoldr :: MonadAsync m => (b -> m (Maybe (a, b))) -> b -> AsyncT m a
726−unfoldr step = go
727− where
728− go s = AsyncT $ \_ stp yld -> do
729− mayb <- step s
730− case mayb of
731− Nothing -> stp
732− Just (a, b) -> yld a (Just (go b))
733−
734−------------------------------------------------------------------------------
735−-- Transformation
736−------------------------------------------------------------------------------
737−
738−-- | Take first 'n' elements from the stream and discard the rest.
739−take :: MonadAsync m => Int -> AsyncT m a -> AsyncT m a
740−take n m = AsyncT $ \ctx stp yld -> do
741− let yield a Nothing = yld a Nothing
742− yield a (Just x) = yld a (Just (take (n - 1) x))
743− if (n <= 0)
744− then stp
745− else (runAsyncT m) ctx stp yield
746−
747−-- | Discard first 'n' elements from the stream and take the rest.
748−drop :: MonadAsync m => Int -> AsyncT m a -> AsyncT m a
749−drop n m = AsyncT $ \ctx stp yld -> do
750− let yield _ Nothing = stp
751− yield _ (Just x) = (runAsyncT $ drop (n - 1) x) ctx stp yld
752− if (n <= 0)
753− then (runAsyncT m) ctx stp yld
754− else (runAsyncT m) ctx stp yield
755−
756−------------------------------------------------------------------------------
757−-- Zipping Streams
758−------------------------------------------------------------------------------
759−
760−-- | Zip two AsyncT streams serially using a monadic zipping function.
761−zipWithM :: MonadAsync m =>
762− (a -> b -> AsyncT m c) -> AsyncT m a -> AsyncT m b -> AsyncT m c
763−zipWithM f m1 m2 = AsyncT $ \_ stp yld -> do
764− let merge a ra =
765− let yield2 b Nothing = (runAsyncT (f a b)) Nothing stp yld
766− yield2 b (Just rb) =
767− (runAsyncT ((f a b) <> (zipWithM f ra rb))) Nothing stp yld
768− in (runAsyncT m2) Nothing stp yield2
769− let yield1 a Nothing = merge a empty
770− yield1 a (Just ra) = merge a ra
771− (runAsyncT m1) Nothing stp yield1
772−
773−-- | Zip two AsyncT streams serially using a pure zipping function.
774−zipWith :: MonadAsync m
775− => (a -> b -> c) -> AsyncT m a -> AsyncT m b -> AsyncT m c
776−zipWith f m1 m2 = AsyncT $ \_ stp yld -> do
777− let merge a ra =
778− let yield2 b Nothing = yld (f a b) Nothing
779− yield2 b (Just rb) = yld (f a b) (Just (zipWith f ra rb))
780− in (runAsyncT m2) Nothing stp yield2
781− let yield1 a Nothing = merge a empty
782− yield1 a (Just ra) = merge a ra
783− (runAsyncT m1) Nothing stp yield1
784−
785−-- | Wrapper around AsyncT type with a serial zipping Applicative instance.
786−-- Note that the binary function interleave (\<=>) is a special case of
787−-- ZipSerial Applicative.
788−--
789−-- > f <$> ZipSerial xs1 <*> ... <*> ZipSerial xsN
790−newtype ZipSerial m a = ZipSerial {getZipSerial :: AsyncT m a}
791− deriving (Functor)
792−
793−instance MonadAsync m => Applicative (ZipSerial m) where
794− pure a = ZipSerial $ cycle1 (pure a)
795− (ZipSerial xs) <*> (ZipSerial ys) = ZipSerial (zipWith id xs ys)
796−
797−-- | The computation specified in the argument is pushed to a new thread and
798−-- the context is computation pulls output from the thread.
799−pushOneToCtx :: MonadAsync m => CtxType -> AsyncT m a -> m (Context m a)
800−pushOneToCtx ctype m = do
801− ctx <- liftIO $
802− case ctype of
803− CtxType _ FIFO -> do
804− c <- getFifoCtx ctype
805− return c
806− CtxType _ LIFO -> do
807− c <- getLifoCtx ctype
808− return c
809− -- Note: We must have all the work on the queue before sending the
810− -- pushworker, otherwise the pushworker may exit before we even get a
811− -- chance to push.
812− liftIO $ (enqueue ctx) m
813− pushWorker ctx
814− return ctx
815−
816−-- XXX The async API is useful for exploring each stream arbitrarily when
817−-- zipping or merging two streams. We can use a newtype wrapper with a monad
818−-- instance that composes like regular streaming libraries to facilitate linear
819−-- composition. We will also need a yield API for that.
820−
821−-- | Run a computation asynchronously, triggers the computation and returns
822−-- another computation (i.e. a promise) that when executed produces the output
823−-- from the original computation. Note that the returned action must be
824−-- executed exactly once and drained completely. If not executed or not drained
825−-- fully we will may have a thread blocked forever and if executed more than
826−-- once a ContextUsedAfterEOF exception will be raised.
827−
828−async :: MonadAsync m => AsyncT m a -> m (AsyncT m a)
829−async m = do
830− ctx <- pushOneToCtx (CtxType Disjunction LIFO) m
831− return $ pullFromCtx ctx
832−
833−-- | Zip two AsyncT streams asyncly (i.e. both the streams are generated
834−-- concurrently) using a monadic zipping function.
835−zipAsyncWithM :: MonadAsync m
836− => (a -> b -> AsyncT m c) -> AsyncT m a -> AsyncT m b -> AsyncT m c
837−zipAsyncWithM f m1 m2 = AsyncT $ \_ stp yld -> do
838− ma <- async m1
839− mb <- async m2
840− (runAsyncT (zipWithM f ma mb)) Nothing stp yld
841−
842−-- | Zip two AsyncT streams asyncly (i.e. both the streams are generated
843−-- concurrently) using a pure zipping function.
844−zipAsyncWith :: MonadAsync m
845− => (a -> b -> c) -> AsyncT m a -> AsyncT m b -> AsyncT m c
846−zipAsyncWith f m1 m2 = AsyncT $ \_ stp yld -> do
847− ma <- async m1
848− mb <- async m2
849− (runAsyncT (zipWith f ma mb)) Nothing stp yld
850−
851−-- | Wrapper around AsyncT type with a parallel zipping Applicative instance.
852−-- Note that the binary operator (\<|>) from the Alternative instance of AsyncT
853−-- is a special case of ZipAsync Applicative.
854−--
855−-- > f <$> ZipAsync xs1 <*> ... <*> ZipAsync xsN
856−newtype ZipAsync m a = ZipAsync {getZipAsync :: AsyncT m a}
857− deriving (Functor)
858−
859−instance MonadAsync m => Applicative (ZipAsync m) where
860− pure a = ZipAsync $ cycle1 (pure a)
861− (ZipAsync xs) <*> (ZipAsync ys) = ZipAsync (zipAsyncWith id xs ys)
862−
863------------------------------------------------------------------------------
731------------------------------------------------------------------------------
864-- Utilities
732-- Utilities
865------------------------------------------------------------------------------
733------------------------------------------------------------------------------
test/Main.hs+9−9
@@ -6,10 +6,10 @@ module Main (main) where
6import Control.Concurrent (threadDelay)
6import Control.Concurrent (threadDelay)
7import Data.Foldable (forM_)
7import Data.Foldable (forM_)
8import Data.List (sort)
8import Data.List (sort)
9−import Prelude hiding (take, drop, zipWith)
10import Test.Hspec
9import Test.Hspec
11
10
12import Asyncly
11import Asyncly
12+import qualified Asyncly.Prelude as A
13
13
14main :: IO ()
14main :: IO ()
15main = hspec $ do
15main = hspec $ do
@@ -206,9 +206,9 @@ main = hspec $ do
206
206
207 describe "Transformation" $ transformOps (<>)
207 describe "Transformation" $ transformOps (<>)
208 describe "Serial zipping" $
208 describe "Serial zipping" $
209− zipOps zipWith zipWithM ZipSerial getZipSerial
209+ zipOps A.zipWith A.zipWithM A.ZipSerial A.getZipSerial
210 describe "Async zipping" $
210 describe "Async zipping" $
211− zipOps zipAsyncWith zipAsyncWithM ZipAsync getZipAsync
211+ zipOps A.zipAsyncWith A.zipAsyncWithM A.ZipAsync A.getZipAsync
212
212
213zipOps :: Applicative (f IO)
213zipOps :: Applicative (f IO)
214 => (forall a b c. (a -> b -> c)
214 => (forall a b c. (a -> b -> c)
@@ -444,21 +444,21 @@ mixedOps = do
444transformOps :: (AsyncT IO Int -> AsyncT IO Int -> AsyncT IO Int) -> Spec
444transformOps :: (AsyncT IO Int -> AsyncT IO Int -> AsyncT IO Int) -> Spec
445transformOps f = do
445transformOps f = do
446 it "take all" $
446 it "take all" $
447− (toList $ take 10 $ foldMapWith f return [1..10])
447+ (toList $ A.take 10 $ foldMapWith f return [1..10])
448 `shouldReturn` [1..10]
448 `shouldReturn` [1..10]
449 it "take none" $
449 it "take none" $
450− (toList $ take 0 $ foldMapWith f return [1..10])
450+ (toList $ A.take 0 $ foldMapWith f return [1..10])
451 `shouldReturn` []
451 `shouldReturn` []
452 it "take 5" $
452 it "take 5" $
453− (toList $ take 5 $ foldMapWith f return [1..10])
453+ (toList $ A.take 5 $ foldMapWith f return [1..10])
454 `shouldReturn` [1..5]
454 `shouldReturn` [1..5]
455
455
456 it "drop all" $
456 it "drop all" $
457− (toList $ drop 10 $ foldMapWith f return [1..10])
457+ (toList $ A.drop 10 $ foldMapWith f return [1..10])
458 `shouldReturn` []
458 `shouldReturn` []
459 it "drop none" $
459 it "drop none" $
460− (toList $ drop 0 $ foldMapWith f return [1..10])
460+ (toList $ A.drop 0 $ foldMapWith f return [1..10])
461 `shouldReturn` [1..10]
461 `shouldReturn` [1..10]
462 it "drop 5" $
462 it "drop 5" $
463− (toList $ drop 5 $ foldMapWith f return [1..10])
463+ (toList $ A.drop 5 $ foldMapWith f return [1..10])
464 `shouldReturn` [6..10]
464 `shouldReturn` [6..10]