Concurrential 0.4.0.0 → 0.5.0.0
raw patch · 2 files changed
+59/−95 lines, 2 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Control.Concurrent.Concurrential: instance Applicative m => Applicative (Concurrential m)
- Control.Concurrent.Concurrential: instance Applicative m => Monad (Concurrential m)
- Control.Concurrent.Concurrential: instance Functor m => Functor (Concurrential m)
- Control.Concurrent.Concurrential: runConcurrentialSimple :: Concurrential IO t -> (Async t -> IO r) -> IO r
- Control.Concurrent.Concurrential: type Joiner g = forall a. g (IO a) -> IO (g a)
- Control.Concurrent.Concurrential: type Runner f g = forall a. f a -> IO (g a)
+ Control.Concurrent.Concurrential: ConcurrentialAp :: Concurrential t -> ConcurrentialAp t
+ Control.Concurrent.Concurrential: concurrentially :: ConcurrentialAp t -> Concurrential t
+ Control.Concurrent.Concurrential: instance Applicative Concurrential
+ Control.Concurrent.Concurrential: instance Applicative ConcurrentialAp
+ Control.Concurrent.Concurrential: instance Functor Concurrential
+ Control.Concurrent.Concurrential: instance Functor ConcurrentialAp
+ Control.Concurrent.Concurrential: instance Monad Concurrential
+ Control.Concurrent.Concurrential: instance Typeable ConcurrentialAp
+ Control.Concurrent.Concurrential: instance Typeable SomeAsync
+ Control.Concurrent.Concurrential: newtype ConcurrentialAp t
- Control.Concurrent.Concurrential: concurrently :: m t -> Concurrential m t
+ Control.Concurrent.Concurrential: concurrently :: IO t -> ConcurrentialAp t
- Control.Concurrent.Concurrential: data Concurrential m t
+ Control.Concurrent.Concurrential: data Concurrential t
- Control.Concurrent.Concurrential: runConcurrential :: (Functor f, Applicative f, Monad f) => Joiner f -> Runner m f -> Concurrential m t -> (Async (f t) -> IO (f r)) -> IO (f r)
+ Control.Concurrent.Concurrential: runConcurrential :: Concurrential t -> (Async t -> IO r) -> IO r
- Control.Concurrent.Concurrential: sequentially :: m t -> Concurrential m t
+ Control.Concurrent.Concurrential: sequentially :: IO t -> ConcurrentialAp t
Files
- Concurrential.cabal +1/−1
- Control/Concurrent/Concurrential.hs +58/−94
Concurrential.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: Concurrential-version: 0.4.0.0+version: 0.5.0.0 synopsis: Mix concurrent and sequential computation -- description: homepage: http://github.com/avieth/Concurrential
Control/Concurrent/Concurrential.hs view
@@ -8,15 +8,19 @@ Portability : non-portable (GHC only) The functions @sequentially@ and @concurrently@ inject @IO@ terms into the-@Concurrential@ monad. This monad's Applicative instance will exploit as-much concurrency as possible, much like the @Concurrently@ monad from async,-such that all @sequentially@ terms will be run in the order in which they-would have been run had they been typical IOs.+@ConcurrentialAp@ applicative functor, whose applicative instance will exploit+as much concurrency as possible such that all @sequentially@ terms will be run+in the order in which they would have been run had they been typical IOs.++Terms of @ConcurrentialAp@ can be transformed into terms of @Concurrential@,+which is a monad. The order of sequential terms is respected even through+binds; a sequential term will not be evaluted until all binds appearing+syntactically earlier than it have been expanded. -} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE AutoDeriveTypeable #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -24,15 +28,13 @@ module Control.Concurrent.Concurrential ( Concurrential-- , Runner- , Joiner+ , ConcurrentialAp(ConcurrentialAp) , runConcurrential- , runConcurrentialSimple , sequentially , concurrently+ , concurrentially , wait @@ -42,12 +44,12 @@ import Control.Monad import Control.Concurrent.MVar import Control.Concurrent.Async hiding (concurrently)-import Control.Exception-import Data.Typeable +-- | An Async without a type parameter, which can be waited for. data SomeAsync where SomeAsync :: Async a -> SomeAsync +-- | Wait for a SomeAsync to complete. waitSomeAsync :: SomeAsync -> IO () waitSomeAsync (SomeAsync async) = wait async >> return () @@ -68,7 +70,6 @@ -- | Description of the way in which a monadic term's evaluation should be -- carried out. data Choice m t = Sequential (m t) | Concurrent (m t)- deriving (Typeable) instance Functor m => Functor (Choice m) where fmap f choice = case choice of@@ -77,86 +78,65 @@ -- | Description of computation which is composed of sequential and concurrent -- parts in some monad @m@.-data Concurrential m t where- SCAtom :: Choice m t -> Concurrential m t- SCBind :: Concurrential m s -> (s -> Concurrential m t) -> Concurrential m t- SCAp :: Concurrential m (r -> t) -> Concurrential m r -> Concurrential m t- deriving (Typeable)+data Concurrential t where+ SCAtom :: Choice IO t -> Concurrential t+ SCBind :: Concurrential s -> (s -> Concurrential t) -> Concurrential t+ SCAp :: Concurrential (r -> t) -> Concurrential r -> Concurrential t -instance Functor m => Functor (Concurrential m) where+instance Functor Concurrential where fmap f sc = case sc of SCAtom choice -> SCAtom $ fmap f choice SCBind sc k -> SCBind sc ((fmap . fmap) f k) SCAp sf sx -> SCAp ((fmap . fmap) f sf) sx -instance Applicative m => Applicative (Concurrential m) where+instance Applicative Concurrential where pure = SCAtom . Sequential . pure- (<*>) = SCAp+ cf <*> cx = SCBind cf (\f -> SCBind cx (\x -> pure (f x))) -instance Applicative m => Monad (Concurrential m) where+instance Monad Concurrential where return = pure (>>=) = SCBind --- | This corresponds to the notion of a common type of monad transformer:--- there is some monad g, and then its associated transformer type f, for--- instance MaybeT = f and Maybe = g--- If we have an--- --- @--- f m a--- @------ then we can get an------ @--- m (g a)--- @------ Here we're interested in the special case where we can achieve IO (g a).--- This does not mean we have to be dealing with an f IO a, it could mean--- that the IO is buried deeper in the transformer stack!------ Motivation: @Async@ functions work with @IO@ and only @IO@, but the @m@--- parameter of a Concurrential may be some other monad which is capable of--- performing @IO@, like @Either String IO@ for instance. In order to run--- computations in this moand through @Async@, we need to know how to get a--- hold of an @IO@. That's what the runner does.-type Runner f g = forall a . f a -> IO (g a)+-- | Concurrential without a Monad instance, but an Applicative instance+-- which exploits concurrency.+newtype ConcurrentialAp t = ConcurrentialAp {+ unConcurrentialAp :: Concurrential t+ } --- | A witness of this type proves that g is in some sense compatible with IO:--- we can bind through it.-type Joiner g = forall a . g (IO a) -> IO (g a)+instance Functor ConcurrentialAp where+ fmap f sc = ConcurrentialAp $ fmap f (unConcurrentialAp sc) +instance Applicative ConcurrentialAp where+ pure = ConcurrentialAp . pure+ cf <*> cx = ConcurrentialAp $ SCAp (unConcurrentialAp cf) (unConcurrentialAp cx)+ -- | Run a Concurrential term with a continuation. We choose CPS here because -- it allows us to explot @withAsync@, giving us a guarantee that an -- exception in a spawning thread will kill spawned threads. runConcurrentialK- :: (Functor f, Applicative f, Monad f)- => Joiner f- -> Runner m f- -> Concurrential m t+ :: Concurrential t -- ^ The computation to run. -> SomeAsync -- ^ The sequential part.- -> ((SomeAsync, Async (f t)) -> IO (f r))+ -> ((SomeAsync, Async t) -> IO r) -- ^ The continuation; fst is sequential part, snd is value part.- -> IO (f r)-runConcurrentialK joiner runner sc sequentialPart k = case sc of+ -> IO r+runConcurrentialK cc sequentialPart k = case cc of SCAtom choice -> case choice of -- The async created becomes the sequential part and the value -- part. So when another Sequential is encountered, its value part -- will have to wait for this computation to complete. Sequential em -> withAsync- (waitSomeAsync sequentialPart >> runner em)+ (waitSomeAsync sequentialPart >> em) (\async -> k (SomeAsync async, async)) -- The async created is the value part, but the sequential part -- remains the same. Concurrent em -> withAsync- (runner em)+ (em) (\async -> k (sequentialPart, async)) SCBind sc next ->- runConcurrentialK joiner runner sc sequentialPart $ \(sequentialPart, asyncS) -> do+ runConcurrentialK sc sequentialPart $ \(sequentialPart, asyncS) -> do synchronizeSequentialPart <- newEmptyMVar let waitAndContinue = do s <- wait asyncS@@ -165,13 +145,10 @@ wait valuePart let continue = \x -> runConcurrentialK- joiner- runner (next x) sequentialPart synchronizeAndWait- let unretracted = fmap continue s- fmap join (joiner unretracted)+ continue s -- This is a very sensitive part of the definition. We fire off a thread -- to wait for @asyncS@ and then continue through @next@, but we also -- create a thread which blocks until the aforementioned has determined@@ -185,44 +162,27 @@ k (SomeAsync sequentialPart, async) SCAp left right ->- runConcurrentialK joiner runner left sequentialPart $ \(sequentialPart, asyncF) ->- runConcurrentialK joiner runner right sequentialPart $ \(sequentialPart, asyncX) ->+ runConcurrentialK left sequentialPart $ \(sequentialPart, asyncF) ->+ runConcurrentialK right sequentialPart $ \(sequentialPart, asyncX) -> let waitAndApply = do f <- wait asyncF x <- wait asyncX- return $ f <*> x+ return $ f x in withAsync waitAndApply (\async -> k (sequentialPart, async)) --- | Run a Concurrential term, realizing the effects of the IO-like terms which+-- | Run a Concurrential term, realizing the effects of the IO terms which -- compose it. runConcurrential- :: (Functor f, Applicative f, Monad f)- => Joiner f- -> Runner m f- -> Concurrential m t- -> (Async (f t) -> IO (f r))+ :: Concurrential t+ -> (Async t -> IO r) -- ^ Similar contract to withAsync; the Async argument is useless outside of -- this function.- -> IO (f r)-runConcurrential joiner runner c k = do+ -> IO r+runConcurrential cc k = do let action = \sequentialPart ->- runConcurrentialK joiner runner c (SomeAsync sequentialPart) (k . snd)+ runConcurrentialK cc (SomeAsync sequentialPart) (k . snd) withAsync (return ()) action -runConcurrentialSimple :: Concurrential IO t -> (Async t -> IO r) -> IO r-runConcurrentialSimple c k = runIdentity <$> runConcurrential simpleJoiner simpleRunner c (continue k)-- where-- continue :: (Async t -> IO r) -> (Async (Identity t) -> IO (Identity r))- continue k = \async -> Identity <$> k (fmap runIdentity async)-- simpleJoiner :: Joiner Identity- simpleJoiner = fmap Identity . runIdentity-- simpleRunner :: Runner IO Identity- simpleRunner = fmap Identity- -- | Create an effect which must be run sequentially. -- If a @sequentially io@ appears in a @Concurrential t@ term then it will -- always be run to completion before any later sequential part of the term@@ -241,9 +201,8 @@ -- important point: @concurrently otherIo@ may be run before, during or after -- @sequentially io@! The ordering through applicative combinators is -- guaranteed only among sequential terms.----sequentially :: m t -> Concurrential m t-sequentially = SCAtom . Sequential+sequentially :: IO t -> ConcurrentialAp t+sequentially = ConcurrentialAp . SCAtom . Sequential -- | Create an effect which is run concurrently where possible, i.e. whenever it -- combined applicatively with other terms. For instance:@@ -256,5 +215,10 @@ -- When running the term @a@, the IO term @io@ will be run concurrently with -- @someConcurrential@, but not so in @b@, because monadic composition has -- been used.-concurrently :: m t -> Concurrential m t-concurrently = SCAtom . Concurrent+concurrently :: IO t -> ConcurrentialAp t+concurrently = ConcurrentialAp . SCAtom . Concurrent++-- | Inject a ConcurrentialAp into Concurrential, losing the+-- concurrency-enabling Applicative instance but gaining a Monad instance.+concurrentially :: ConcurrentialAp t -> Concurrential t+concurrentially = unConcurrentialAp