explicit-exception 0.1.3 → 0.1.4
raw patch · 4 files changed
+225/−69 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Control.Monad.Exception.Asynchronous: ExceptionalT :: m (Exceptional e a) -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: bindT :: (Monad m, Monoid b) => ExceptionalT e m a -> (a -> ExceptionalT e m b) -> ExceptionalT e m b
+ Control.Monad.Exception.Asynchronous: eatNothing :: Exceptional (Maybe e) a -> Exceptional e a
+ Control.Monad.Exception.Asynchronous: eatNothingT :: (Monad m) => ExceptionalT (Maybe e) m a -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: forceT :: (Monad m) => ExceptionalT e m a -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: fromSynchronousMonoidT :: (Functor m, Monoid a) => ExceptionalT e m a -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: fromSynchronousT :: (Functor m) => a -> ExceptionalT e m a -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: instance (Functor m) => Functor (ExceptionalT e m)
+ Control.Monad.Exception.Asynchronous: instance (Monad m, Monoid a) => Monoid (ExceptionalT e m a)
+ Control.Monad.Exception.Asynchronous: manyMonoidT :: (Monad m, Monoid a) => ExceptionalT e m a -> ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: mapExceptionT :: (Monad m) => (e0 -> e1) -> ExceptionalT e0 m a -> ExceptionalT e1 m a
+ Control.Monad.Exception.Asynchronous: mapExceptionalT :: (m (Exceptional e0 a) -> n (Exceptional e1 b)) -> ExceptionalT e0 m a -> ExceptionalT e1 n b
+ Control.Monad.Exception.Asynchronous: newtype ExceptionalT e m a
+ Control.Monad.Exception.Asynchronous: runExceptionalT :: ExceptionalT e m a -> m (Exceptional e a)
+ Control.Monad.Exception.Asynchronous: throwMonoidT :: (Monad m, Monoid a) => e -> ExceptionalT e m a
+ Control.Monad.Exception.Synchronous: manyMonoidT :: (Monad m, Monoid a) => (e0 -> Maybe e1) -> ExceptionalT e0 m a -> ExceptionalT e1 m a
Files
- explicit-exception.cabal +2/−2
- src/Control/Monad/Exception/Asynchronous.hs +181/−63
- src/Control/Monad/Exception/Synchronous.hs +40/−3
- src/System/IO/Exception/TextFile.hs +2/−1
explicit-exception.cabal view
@@ -1,5 +1,5 @@ Name: explicit-exception-Version: 0.1.3+Version: 0.1.4 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de>@@ -46,7 +46,7 @@ Source-Repository this type: darcs location: http://code.haskell.org/explicit-exception/- tag: 0.1.3+ tag: 0.1.4 Library Build-Depends: base >= 2, transformers >=0.0 && <0.2
src/Control/Monad/Exception/Asynchronous.hs view
@@ -5,7 +5,13 @@ TODO: -* Check whether laziness behaviour is reasonable.+* Is it reasonable, that many functions match the exception lazily?+ Or is lazy decoupling an operation that shall always be done explicitly?+* Is the Null type appropriate anywhere?+ Should it be better a Monoid type with mempty?+ Shall Monoid.mempty be the default, or functions with explicit default values?+* Shall we replace Monad constraint by Functor constraint,+ where we only need liftM? -} module Control.Monad.Exception.Asynchronous ( Exceptional(..),@@ -17,8 +23,7 @@ toSynchronous, throw, throwMonoid,- manySynchronousT,- processToSynchronousT_,+ eatNothing, zipWith, append, continue,@@ -36,13 +41,26 @@ swapToSynchronousAsynchronous, swapToAsynchronousSynchronous, + ExceptionalT(..),+ fromSynchronousT,+ fromSynchronousMonoidT,+ forceT,+ mapExceptionT,+ mapExceptionalT,+ throwMonoidT,+ eatNothingT,+ bindT,+ manySynchronousT,+ manyMonoidT,+ processToSynchronousT_,+ appendM, continueM, ) where import qualified Control.Monad.Exception.Synchronous as Sync -import Control.Monad (mplus, liftM, )+import Control.Monad (mplus, liftM, join, ) import Control.Applicative (Applicative, liftA, ) {- import Data.Traversable (Traversable, )@@ -111,61 +129,26 @@ maybe (Sync.Success a) Sync.Exception me +{- |+I think in most cases we want throwMonoid,+thus we can replace 'throw' by 'throwMonoid'.+-} throw :: e -> Exceptional e () throw e = broken e () throwMonoid :: Monoid a => e -> Exceptional e a throwMonoid e = broken e mempty -- {- |-Repeat an action with synchronous exceptions until an exception occurs.-Combine all atomic results using the @bind@ function.-It may be @cons = (:)@ and @empty = []@ for @b@ being a list type.-The @defer@ function may be @id@-or @unsafeInterleaveIO@ for lazy read operations.-The exception is returned as asynchronous exception.--}-manySynchronousT :: (Monad m) =>- (m (Exceptional e b) -> m (Exceptional e b))- {- ^ @defer@ function -} ->- (a -> b -> b) {- ^ @cons@ function -} ->- b {- ^ @empty@ -} ->- Sync.ExceptionalT e m a {- ^ atomic action to repeat -} ->- m (Exceptional e b)-manySynchronousT defer cons empty action =- let recourse =- liftM force $ defer $- do r <- Sync.tryT action- case r of- Sync.Exception e -> return (Exceptional (Just e) empty)- Sync.Success x -> liftM (fmap (cons x)) recourse- in recourse--{- |-Scan @x@ using the @decons@ function-and run an action with synchronous exceptions for each element fetched from @x@.-Each invocation of an element action may stop this function-due to an exception.-If all element action can be performed successfully-and if there is an asynchronous exception-then at the end this exception is raised as synchronous exception.-@decons@ function might be @viewL@.+You might use an exception of type @Maybe e@ in 'manyMonoidT'+in order to stop the loop.+After finishing the loop you will want+to turn the @Nothing@ exception into a success.+This is achieved by this function. -}-processToSynchronousT_ :: (Monad m) =>- (b -> Maybe (a,b)) {- ^ decons function -} ->- (a -> Sync.ExceptionalT e m ())- {- ^ action that is run for each element fetched from @x@ -} ->- Exceptional e b {- ^ value @x@ of type @b@ with asynchronous exception -} ->- Sync.ExceptionalT e m ()-processToSynchronousT_ decons action (Exceptional me x) =- let recourse b0 =- maybe- (maybe (return ()) Sync.throwT me)- (\(a,b1) -> action a >> recourse b1)- (decons b0)- in recourse x+eatNothing :: Exceptional (Maybe e) a -> Exceptional e a+eatNothing (Exceptional e a) =+ Exceptional (join e) a -- ** handling of special result types@@ -250,8 +233,6 @@ {- fmap (f.g) = fmap f . fmap g-- -} instance Functor (Exceptional e) where fmap f ~(Exceptional e a) = Exceptional e (f a)@@ -369,30 +350,41 @@ Sync.Success s -> fmap Sync.Success s -{---- * Monad transformer+-- * Monad/Monoid transformer +{- |+In contrast to synchronous exceptions,+the asynchronous monad transformer is not quite a monad.+You must use the 'Monoid' interface or 'bindT' instead.+-} newtype ExceptionalT e m a = ExceptionalT {runExceptionalT :: m (Exceptional e a)} fromSynchronousT :: Functor m => a -> Sync.ExceptionalT e m a -> ExceptionalT e m a-fromSynchronousT deflt (Sync.ExceptionalT mx) =- ExceptionalT $ fmap (fromSynchronous deflt) mx----throwT :: (Monad m) =>- e -> ExceptionalT e m ()-throwT = ExceptionalT . return . throw+fromSynchronousT deflt =+ ExceptionalT .+ fmap (fromSynchronous deflt) .+ Sync.runExceptionalT +fromSynchronousMonoidT :: (Functor m, Monoid a) =>+ Sync.ExceptionalT e m a -> ExceptionalT e m a+fromSynchronousMonoidT =+ fromSynchronousT mempty instance Functor m => Functor (ExceptionalT e m) where fmap f (ExceptionalT x) = ExceptionalT (fmap (fmap f) x) +instance (Monad m, Monoid a) => Monoid (ExceptionalT e m a) where+ mempty = ExceptionalT $ return mempty+ mappend x y =+ ExceptionalT $+ appendM (runExceptionalT x) (runExceptionalT y)++{- instance Applicative m => Applicative (ExceptionalT e m) where pure = ExceptionalT . pure . pure ExceptionalT f <*> ExceptionalT x =@@ -407,6 +399,58 @@ return $ Exceptional (ex ++ ey) y -} ++{- |+see 'force'+-}+forceT :: Monad m => ExceptionalT e m a -> ExceptionalT e m a+forceT =+ ExceptionalT . liftM force . runExceptionalT++mapExceptionT :: (Monad m) =>+ (e0 -> e1) ->+ ExceptionalT e0 m a ->+ ExceptionalT e1 m a+mapExceptionT f =+ ExceptionalT . liftM (mapException f) . runExceptionalT++mapExceptionalT ::+ (m (Exceptional e0 a) -> n (Exceptional e1 b)) ->+ ExceptionalT e0 m a -> ExceptionalT e1 n b+mapExceptionalT f =+ ExceptionalT . f . runExceptionalT+++throwMonoidT :: (Monad m, Monoid a) =>+ e -> ExceptionalT e m a+throwMonoidT = ExceptionalT . return . throwMonoid+++eatNothingT :: Monad m =>+ ExceptionalT (Maybe e) m a -> ExceptionalT e m a+eatNothingT =+ mapExceptionalT (liftM eatNothing)+++infixl 1 `bindT`++{- |+The monadic bind operation.+It cannot be made an instance of the Monad class method @(>>=)@+since it requires a default return value+in case the first action fails.+We get this default value by the 'Monoid' method 'mempty'.+-}+bindT :: (Monad m, Monoid b) =>+ ExceptionalT e m a ->+ (a -> ExceptionalT e m b) ->+ ExceptionalT e m b+bindT x y =+ ExceptionalT $+ runExceptionalT x >>= \r ->+ runExceptionalT $ maybe (y $ result r) throwMonoidT (exception r)++ infixr 1 {- `bindM`, -} `appendM`, `continueM` {-@@ -435,3 +479,77 @@ Maybe e -> m (Exceptional e a) -> m (Exceptional e a) continueMPlain x y = maybe y (return . throwMonoid) x+++{- |+Repeat an action with synchronous exceptions until an exception occurs.+Combine all atomic results using the @bind@ function.+It may be @cons = (:)@ and @empty = []@ for @b@ being a list type.+The @defer@ function may be @id@+or @unsafeInterleaveIO@ for lazy read operations.+The exception is returned as asynchronous exception.+-}+manySynchronousT :: (Monad m) =>+ (m (Exceptional e b) -> m (Exceptional e b))+ {- ^ @defer@ function -} ->+ (a -> b -> b) {- ^ @cons@ function -} ->+ b {- ^ @empty@ -} ->+ Sync.ExceptionalT e m a {- ^ atomic action to repeat -} ->+ m (Exceptional e b)+manySynchronousT defer cons empty action =+ let recourse =+ liftM force $ defer $+ do r <- Sync.tryT action+ case r of+ Sync.Exception e -> return (Exceptional (Just e) empty)+ Sync.Success x -> liftM (fmap (cons x)) recourse+ in recourse++{-# DEPRECATED manySynchronousT "use manyMonoidT with appropriate Monad like LazyIO and result Monoid like Endo instead" #-}++{- |+We advise to use the Endo Monoid+when you want to read a series of characters into a list.+This means you use the difference lists technique+in order to build the list, which is efficient.++> import Data.Monoid (Endo, appEndo, )+> import Control.Exception (try, )+> import qualified Control.Monad.Exception.Synchronous as Sync++> fmap (flip appEndo []) $ manyMonoidT (fromSynchronousMonoidT $ fmap (Endo . (:)) $ Sync.fromEitherT $ try getChar)++If you want Lazy IO you must additionally convert @getChar@ to LazyIO monad.+-}+manyMonoidT :: (Monad m, Monoid a) =>+ ExceptionalT e m a {- ^ atomic action to repeat -} ->+ ExceptionalT e m a+manyMonoidT act =+ let -- like fmap, but doesn't require Functor instance of @m@+ customFmap f = mapExceptionalT (liftM (fmap f))+ go = act `bindT` \r -> customFmap (mappend r) go+ in go++{- |+Scan @x@ using the @decons@ function+and run an action with synchronous exceptions for each element fetched from @x@.+Each invocation of an element action may stop this function+due to an exception.+If all element actions can be performed successfully+and if there is an asynchronous exception+then at the end this exception is raised as synchronous exception.+@decons@ function might be @Data.List.HT.viewL@.+-}+processToSynchronousT_ :: (Monad m) =>+ (b -> Maybe (a,b)) {- ^ decons function -} ->+ (a -> Sync.ExceptionalT e m ())+ {- ^ action that is run for each element fetched from @x@ -} ->+ Exceptional e b {- ^ value @x@ of type @b@ with asynchronous exception -} ->+ Sync.ExceptionalT e m ()+processToSynchronousT_ decons action (Exceptional me x) =+ let recourse b0 =+ maybe+ (maybe (return ()) Sync.throwT me)+ (\(a,b1) -> action a >> recourse b1)+ (decons b0)+ in recourse x
src/Control/Monad/Exception/Synchronous.hs view
@@ -1,6 +1,31 @@ {- | Synchronous exceptions immediately abort a series of computations. We provide monads for describing this behaviour.+In contrast to ErrorT from @mtl@ or @transformers@ package+we do not pose restrictions on the exception type.++How to tell, that a function can possibly throw more than one (kind of) exception?++If you would use the exception type @(Either ParserException IOError)@+then this is different from @(Either IOError ParserException)@.+Thus we recommned using type classes for exceptions.+Then you can use one type containing all exceptions in an application,+but the type signature still tells which exceptions are actually possible.+Examples:++> parser :: ParserException e => ExceptionalT e ParserMonad a+>+> getLine :: IOException e => ExceptionalT e IO String+>+> fileParser :: (ParserException e, IOException e) => ExceptionalT e IO String++Unfortunately, this way you cannot remove single exceptions+from the constraints by catching them.+You can only remove all of them using 'resolve' or none.+For a more advanced approach,+that allows removing exceptions constraints+by some non-Haskell-98 type hackery,+see the exception package by Joseph Iborra. -} module Control.Monad.Exception.Synchronous ( Exceptional(..),@@ -29,6 +54,7 @@ resolveT, tryT, manyT,+ manyMonoidT, ) where import Control.Applicative (Applicative(pure, (<*>)))@@ -36,6 +62,7 @@ import Control.Monad.Fix (MonadFix, mfix, ) import Control.Monad.Trans (MonadTrans, lift, {- MonadIO(liftIO), -} ) import Control.Monad.Trans.Error (ErrorT(ErrorT, runErrorT))+import Data.Monoid(Monoid, mappend, mempty, Endo(Endo, appEndo), ) import Prelude hiding (catch, ) @@ -281,13 +308,23 @@ ExceptionalT e0 m a {- ^ atomic action to repeat -} -> ExceptionalT e1 m b manyT handler cons empty action =+ liftM (flip appEndo empty) $+ manyMonoidT handler $+ liftM (Endo . cons) action++manyMonoidT :: (Monad m, Monoid a) =>+ (e0 -> Maybe e1) {- ^ exception handler -} ->+ ExceptionalT e0 m a {- ^ atomic action to repeat -} ->+ ExceptionalT e1 m a+manyMonoidT handler action = let recourse = do r <- lift $ tryT action case r of- Exception e -> maybe (return empty) throwT (handler e)- Success x -> liftM (cons x) recourse+ -- Exception e -> maybe (return empty) throwT (handler e)+ -- more lazy+ Exception e -> ExceptionalT $ return $ maybe (Success mempty) throw (handler e)+ Success x -> liftM (mappend x) recourse in recourse-
src/System/IO/Exception/TextFile.hs view
@@ -33,13 +33,14 @@ getContentsSynchronous :: Handle -> EIO String getContentsSynchronous h = Sync.manyT- -- candidate for toMaybe+ -- candidate for toMaybe from utility-ht (\e -> if isEOFError e then Nothing else Just e) (:) [] (getChar h) {- | This calls 'unsafeInterleaveIO'. Maybe we should also attach 'unsafe' to this function name?+We should use the LazyIO type and manyT here. -} getContentsAsynchronous :: Handle -> SIO (Async.Exceptional IOException String) getContentsAsynchronous h =