module Control.Concurrent.Conceit (
Conceit (..)
, _Conceit
, conceit
, mapConceit
) where
import Data.Bifunctor
import Data.Monoid
import Data.Typeable
import Data.Traversable
import Control.Applicative
import Control.Monad
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
revealError :: (Show e, Typeable e) => IO a -> IO (Either e a)
revealError action = catch (action >>= return . Right)
(\(WrappedError e) -> return . Left $ e)
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
pure = Conceit . pure . pure
Conceit fs <*> Conceit as =
Conceit . revealError $
uncurry ($) <$> concurrently (elideError fs) (elideError as)
instance (Show e, Typeable e) => Alternative (Conceit e) where
empty = Conceit $ forever (threadDelay maxBound)
Conceit as <|> Conceit bs =
Conceit $ either id id <$> race as bs
instance (Show e, Typeable e, Monoid a) => Monoid (Conceit e a) where
mempty = Conceit . pure . pure $ mempty
mappend c1 c2 = (<>) <$> c1 <*> c2
_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
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)