packages feed

conceit 0.1.1.0 → 0.2.0.0

raw patch · 3 files changed

+137/−35 lines, 3 filesdep +exceptionsdep +mtldep +semigroupoidsdep −asyncPVP ok

version bump matches the API change (PVP)

Dependencies added: exceptions, mtl, semigroupoids, void

Dependencies removed: async

API changes (from Hackage documentation)

- Control.Concurrent.Conceit: instance (Show e, Typeable e) => Alternative (Conceit e)
- Control.Concurrent.Conceit: instance (Show e, Typeable e) => Applicative (Conceit e)
- Control.Concurrent.Conceit: instance (Show e, Typeable e) => Exception (WrappedError e)
- Control.Concurrent.Conceit: instance (Show e, Typeable e, Monoid a) => Monoid (Conceit e a)
- Control.Concurrent.Conceit: instance Show e => Show (WrappedError e)
- Control.Concurrent.Conceit: instance Typeable1 WrappedError
+ Control.Concurrent.Conceit: _runConceit :: Conceit Void a -> IO a
+ Control.Concurrent.Conceit: instance Alt (Conceit e)
+ Control.Concurrent.Conceit: instance Alternative (Conceit e)
+ Control.Concurrent.Conceit: instance Applicative (Conceit e)
+ Control.Concurrent.Conceit: instance Apply (Conceit s)
+ Control.Concurrent.Conceit: instance Bind (Conceit s)
+ Control.Concurrent.Conceit: instance Monad (Conceit e)
+ Control.Concurrent.Conceit: instance MonadCatch (Conceit e)
+ Control.Concurrent.Conceit: instance MonadError e (Conceit e)
+ Control.Concurrent.Conceit: instance MonadIO (Conceit e)
+ Control.Concurrent.Conceit: instance MonadPlus (Conceit e)
+ Control.Concurrent.Conceit: instance MonadThrow (Conceit e)
+ Control.Concurrent.Conceit: instance Monoid a => Monoid (Conceit e a)
+ Control.Concurrent.Conceit: instance Plus (Conceit e)
- Control.Concurrent.Conceit: conceit :: (Show e, Typeable e) => IO (Either e a) -> IO (Either e b) -> IO (Either e (a, b))
+ Control.Concurrent.Conceit: conceit :: IO (Either e a) -> IO (Either e b) -> IO (Either e (a, b))
- Control.Concurrent.Conceit: mapConceit :: (Show e, Typeable e, Traversable t) => (a -> IO (Either e b)) -> t a -> IO (Either e (t b))
+ Control.Concurrent.Conceit: mapConceit :: Traversable t => (a -> IO (Either e b)) -> t a -> IO (Either e (t b))

Files

CHANGELOG view
@@ -1,3 +1,10 @@+0.2.0.0+=======+    - Internal changes that remove the necessity for (Show e,Typeable e)+      constraints.+    - Removed async dependency.+    - Many new instances for Conceit.+ 0.1.1.0 ======= 
conceit.cabal view
@@ -1,5 +1,5 @@ name:          conceit-version:       0.1.1.0+version:       0.2.0.0 license:       BSD3 license-file:  LICENSE data-files:    @@ -23,8 +23,11 @@     other-modules:      build-depends:                  base >= 4.4 && < 5,+        semigroupoids ==4.*,         bifunctors >= 4.1 && < 5,-        async >= 2.0.1 && < 2.1+        void >= 0.6 && < 0.7,+        exceptions >= 0.6 && < 0.7,+        mtl >=2.0 && <2.3   Source-repository head     type:     git
src/Control/Concurrent/Conceit.hs view
@@ -1,9 +1,13 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE CPP #-}  module Control.Concurrent.Conceit (            Conceit (..)         , _Conceit+        , _runConceit         , conceit         , mapConceit     ) where@@ -12,71 +16,159 @@ import Data.Monoid import Data.Typeable import Data.Traversable-import Control.Applicative+import Data.Void+import Control.Applicative  import Control.Monad-import Control.Exception+import qualified Control.Monad.Catch as Ex+import Control.Exception  import Control.Concurrent-import Control.Concurrent.Async--data WrappedError e = WrappedError e-    deriving (Show, Typeable)--instance (Show e, Typeable e) => Exception (WrappedError e)--elideError :: (Show e, Typeable e) => IO (Either e a) -> IO a-elideError action = action >>= either (throwIO . WrappedError) return+import Data.Functor.Bind+import Data.Functor.Plus -revealError :: (Show e, Typeable e) => IO a -> IO (Either e a)  -revealError action = catch (action >>= return . Right)-                           (\(WrappedError e) -> return . Left $ e)   +#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@. -   The 'Applicative' instance is used to run actions concurrently, wait until-they finish, and combine their results. +   The 'Applicative' instance runs two actions concurrently, waits until+they finish, and combines their results.  -   However, if any of the actions fails with @e@ the other actions are-immediately cancelled and the whole computation fails with @e@. +   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 -instance (Show e, Typeable e) => Applicative (Conceit e) where+instance Applicative (Conceit e) where   pure = Conceit . pure . pure   Conceit fs <*> Conceit as =-    Conceit . revealError $ -        uncurry ($) <$> concurrently (elideError fs) (elideError as)+         Conceit $ fmap (fmap (\(f, a) -> f a)) $ conceit fs as -instance (Show e, Typeable e) => Alternative (Conceit e) where+instance Alternative (Conceit e) where   empty = Conceit $ forever (threadDelay maxBound)   Conceit as <|> Conceit bs =-    Conceit $ either id id <$> race as bs+    Conceit $ fmap (fmap (either id id)) $ race as bs -instance (Show e, Typeable e, Monoid a) => Monoid (Conceit e a) where+instance (Monoid a) => Monoid (Conceit e a) where    mempty = Conceit . pure . pure $ mempty    mappend c1 c2 = (<>) <$> c1 <*> c2 +-- | `>>` is concurrent.+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+   f >> k = f *> k++instance MonadPlus (Conceit e) where+   mzero = empty+   mplus = (<|>)++instance MonadIO (Conceit e) where+   liftIO = _Conceit++-- | `<!>` makes its two arguments race against each other.+instance 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)+ _Conceit :: IO a -> Conceit e a _Conceit = Conceit . fmap pure   -conceit :: (Show e, Typeable e) -        => IO (Either e a)-        -> IO (Either e b)-        -> IO (Either e (a,b))-conceit c1 c2 = runConceit $ (,) <$> Conceit c1 <*> Conceit c2+_runConceit :: Conceit Void a -> IO a+_runConceit c = either absurd id <$> runConceit c   {-|        Works similarly to 'Control.Concurrent.Async.mapConcurrently' from the @async@ package, but if any of the computations fails with @e@, the others are immediately cancelled and the whole computation fails with @e@.   -}-mapConceit :: (Show e, Typeable e, Traversable t) => (a -> IO (Either e b)) -> t a -> IO (Either e (t b))-mapConceit f = revealError .  mapConcurrently (elideError . f)+mapConceit :: (Traversable t) => (a -> IO (Either e b)) -> t a -> IO (Either e (t b))+mapConceit f = runConceit . sequenceA . fmap (Conceit . f)++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+  where+    collect m = do+        e <- takeMVar m+        case e of+            Left ex -> throwIO ex+            Right (Right (Right r1)) -> return $ Right $ Right r1+            Right (Right (Left e1)) -> return $ Left e1 +            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 [])+  where+    collect [Left (Right a), Right (Right b)] _ = return $ Right (a,b)+    collect [Right (Right b), Left (Right a)] _ = return $ Right (a,b)+    collect (Left (Left ea):_) _ = return $ Left ea+    collect (Right (Left eb):_) _ = return $ Left eb+    collect xs m = do+        e <- takeMVar m+        case e of+            Left ex -> throwIO ex+            Right r -> collect (r:xs) m++-- Verbatim copy of the internal concurrently' function from async+conceit' :: IO a -> IO b+         -> (MVar (Either SomeException (Either a b)) -> IO r)+         -> IO r+conceit' left right collect = do+    done <- newEmptyMVar+    mask $ \restore -> do+        lid <- forkIO $ restore (left >>= putMVar done . Right . Left)+                             `catchAll` (putMVar done . Left)+        rid <- forkIO $ restore (right >>= putMVar done . Right . Right)+                             `catchAll` (putMVar done . Left)+        let stop = killThread lid >> killThread rid+        r <- restore (collect done) `onException` stop+        stop+        return r