conceit 0.2.2.1 → 0.5.0.0
raw patch · 4 files changed
Files
- CHANGELOG +22/−0
- README.md +6/−3
- conceit.cabal +12/−15
- src/Control/Concurrent/Conceit.hs +32/−73
CHANGELOG view
@@ -1,3 +1,25 @@+0.5.0.0 +======= + - Removed all dependencies except base. This is a breaking change because + we lose the Alt, Plus and Apply instances. + +0.4.0.0 +======= + - Removed Monad, MonadThrow, MonadCatch instances. + - Added Semigroup instance. + - Removed exception and mtl dependencies. + +0.3.1.0 +======= + - Made internal function "conceit'" public. + +0.3.0.0 +======= + - Incorporate the solution to issue #27 from the async package + https://github.com/simonmar/async/issues/27 + This changes the order of cancellation of threads in case + of an asynchronous exception. + 0.2.2.1 ======= - Qualified Alt to make it compile with GHC 7.10.
README.md view
@@ -1,9 +1,12 @@ conceit ======= -A version of the Concurrently applicative from Simon Marlow's async package, -with the difference that the concurrent computation stops if any of the actions -returns a Left value, not only in the case of exceptions. +A version of the +[Concurrently](http://hackage.haskell.org/package/async-2.0.2/docs/Control-Concurrent-Async.html#t:Concurrently) +Applicative from Simon Marlow's +[async](http://hackage.haskell.org/package/async) package, with the difference +that the concurrent computation stops if any of the actions returns a Left +value, not only in the case of exceptions. The internals have been copied wholesale from Concurrently, with modifications to support the new behaviour.
conceit.cabal view
@@ -1,36 +1,33 @@+cabal-version: 2.4 name: conceit -version: 0.2.2.1 -license: BSD3 +version: 0.5.0.0 +license: BSD-3-Clause license-file: LICENSE data-files: author: Daniel Díaz Carrete maintainer: diaz_carrete@yahoo.com category: Concurrency build-type: Simple -cabal-version: >= 1.10 -Synopsis: Concurrent actions that may fail -Description: A version of the async package's 'Control.Concurrent.Async.Concurrently' for which the actions may fail +synopsis: Concurrent actions that may fail with a value. -Extra-Source-Files: +description: + A version of the async package's 'Control.Concurrent.Async.Concurrently' for + which the actions may fail with a value. + +extra-source-files: README.md CHANGELOG -Library +library default-language: Haskell2010 hs-source-dirs: src exposed-modules: Control.Concurrent.Conceit other-modules: build-depends: - base >= 4.4 && < 5, - semigroupoids ==4.*, - bifunctors >= 4.1 && < 5, - void >= 0.6 && < 1.0, - exceptions >= 0.6 && < 1.0, - mtl >=2.0 && <2.3, - transformers >=0.2 && < 0.5 + base >= 4.8 && < 5 -Source-repository head +source-repository head type: git location: https://github.com/danidiaz/conceit.git
src/Control/Concurrent/Conceit.hs view
@@ -1,8 +1,4 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFunctor #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE CPP #-} module Control.Concurrent.Conceit ( Conceit (..) @@ -10,26 +6,21 @@ , _runConceit , conceit , mapConceit + -- * Internals + -- $internals + , conceit' ) where +import Data.Void import Data.Bifunctor -import Data.Monoid -import Data.Typeable +import Data.Semigroup +import Data.Monoid (Monoid,mempty,mappend) import Data.Traversable -import Data.Void import Control.Applicative -import Control.Monad -import Control.Monad.IO.Class -import qualified Control.Monad.Catch as Ex +import Control.Monad (forever) import Control.Exception import Control.Concurrent -import Data.Functor.Bind -import Data.Functor.Plus -#if MIN_VERSION_mtl(2, 2, 1) -import Control.Monad.Except -#endif - {-| 'Conceit' is very similar to 'Control.Concurrent.Async.Concurrently' from the @async@ package, but it has an explicit error type @e@. @@ -40,81 +31,40 @@ However, if any of the actions fails with @e@ the other action is immediately cancelled and the whole computation fails with @e@. - To put it another way: 'Conceit' behaves like 'Concurrently' for - successes and like 'race' for errors. + To put it another way: 'Conceit' behaves like 'Concurrently' for + successes and like 'race' for errors. -} newtype Conceit e a = Conceit { runConceit :: IO (Either e a) } deriving Functor instance Bifunctor Conceit where - bimap f g (Conceit x) = Conceit $ liftM (bimap f g) x + bimap f g (Conceit x) = Conceit $ liftA (bimap f g) x instance Applicative (Conceit e) where pure = Conceit . pure . pure Conceit fs <*> Conceit as = Conceit $ fmap (fmap (\(f, a) -> f a)) $ conceit fs as +-- | `<|>` makes its two arguments race against each other. instance Alternative (Conceit e) where empty = Conceit $ forever (threadDelay maxBound) Conceit as <|> Conceit bs = Conceit $ fmap (fmap (either id id)) $ race as bs +instance (Semigroup a) => Semigroup (Conceit e a) where + c1 <> c2 = (<>) <$> c1 <*> c2 + instance (Monoid a) => Monoid (Conceit e a) where mempty = Conceit . pure . pure $ mempty - mappend c1 c2 = (<>) <$> c1 <*> c2 --- | `>>` sequences its arguments. -instance Monad (Conceit e) where - return = pure - f >>= k = Conceit $ do - x <- runConceit f - case x of - Left e -> return $ Left e - Right r -> runConceit $ k r - -instance MonadPlus (Conceit e) where - mzero = empty - mplus = (<|>) - -instance MonadIO (Conceit e) where - liftIO = _Conceit - --- | `<!>` makes its two arguments race against each other. -instance Data.Functor.Plus.Alt (Conceit e) where - (<!>) = (<|>) - --- | `zero` is a computation that never finishes. -instance Plus (Conceit e) where - zero = empty - --- | `>>-` is sequential. -instance Bind (Conceit s) where - (>>-) = (>>=) - --- | `<.>` is concurrent. -instance Apply (Conceit s) where - (<.>) = (<*>) - (<.) = (<*) - (.>) = (*>) - - -#if MIN_VERSION_mtl(2, 2, 1) -instance MonadError e (Conceit e) where - throwError = Conceit . pure . Left - Conceit c `catchError` h = - Conceit $ runExceptT $ ExceptT c `catchError` (ExceptT . runConceit . h) -#endif - --- | Throws exceptions into IO. -instance Ex.MonadThrow (Conceit e) where - throwM = Conceit . Ex.throwM - --- | Catches exceptions from IO. -instance Ex.MonadCatch (Conceit e) where - catch (Conceit m) f = Conceit $ Ex.catch m (runConceit . f) - +{-| + Construct a 'Conceit' as if it were a 'Control.Concurrent.Async.Concurrently'. +-} _Conceit :: IO a -> Conceit e a _Conceit = Conceit . fmap pure +{-| + Run a 'Conceit' as if it were a 'Control.Concurrent.Async.Concurrently'. +-} _runConceit :: Conceit Void a -> IO a _runConceit c = either absurd id <$> runConceit c @@ -129,6 +79,7 @@ catchAll :: IO a -> (SomeException -> IO a) -> IO a catchAll = catch + -- Adapted from the race function from async race :: IO (Either e a) -> IO (Either e b) -> IO (Either e (Either a b)) race left right = conceit' left right collect @@ -142,6 +93,7 @@ Right (Left (Right r2)) -> return $ Right $ Left r2 Right (Left (Left e2)) -> return $ Left e2 + -- Adapted from the concurrently function from async conceit :: IO (Either e a) -> IO (Either e b) -> IO (Either e (a, b)) conceit left right = conceit' left right (collect []) @@ -156,8 +108,13 @@ Left ex -> throwIO ex Right r -> collect (r:xs) m --- Verbatim copy of the internal concurrently' function from async -conceit' :: IO a -> IO b + +{-| + Verbatim copy of the internal @concurrently'@ function from the @async@ + package. +-} +conceit' :: IO a + -> IO b -> (MVar (Either SomeException (Either a b)) -> IO r) -> IO r conceit' left right collect = do @@ -167,7 +124,9 @@ `catchAll` (putMVar done . Left) rid <- forkIO $ restore (right >>= putMVar done . Right . Right) `catchAll` (putMVar done . Left) - let stop = killThread lid >> killThread rid + let stop = killThread rid >> killThread lid + -- kill right before left, to match the semantics of + -- the version using withAsync. r <- restore (collect done) `onException` stop stop return r