explicit-exception 0.0.2 → 0.1
raw patch · 4 files changed
+114/−30 lines, 4 filesdep +transformersdep −mtlPVP ok
version bump matches the API change (PVP)
Dependencies added: transformers
Dependencies removed: mtl
API changes (from Hackage documentation)
+ Control.Monad.Exception.Asynchronous: mapException :: (e0 -> e1) -> Exceptional e0 a -> Exceptional e1 a
+ Control.Monad.Exception.Asynchronous: mapExceptional :: (e0 -> e1) -> (a -> b) -> Exceptional e0 a -> Exceptional e1 b
+ Control.Monad.Exception.Asynchronous: maybeAbort :: Exceptional e a -> Maybe e -> Exceptional e a
+ Control.Monad.Exception.Asynchronous: simultaneousBind :: Exceptional e a -> (a -> Exceptional e b) -> Exceptional e b
+ Control.Monad.Exception.Asynchronous: simultaneousBindM :: (Monad m) => m (Exceptional e a) -> (a -> m (Exceptional e b)) -> m (Exceptional e b)
+ Control.Monad.Exception.Asynchronous: swapToAsynchronousSynchronous :: Exceptional e1 (Exceptional e0 a) -> Exceptional e0 (Exceptional e1 a)
+ Control.Monad.Exception.Asynchronous: swapToSynchronousAsynchronous :: Exceptional e0 (Exceptional e1 a) -> Exceptional e1 (Exceptional e0 a)
Files
- explicit-exception.cabal +12/−4
- src/Control/Monad/Exception/Asynchronous.hs +78/−21
- src/Control/Monad/Exception/Synchronous.hs +18/−3
- src/Control/Monad/Label.hs +6/−2
explicit-exception.cabal view
@@ -1,11 +1,10 @@ Name: explicit-exception-Version: 0.0.2+Version: 0.1 License: BSD3 License-File: LICENSE Author: Henning Thielemann <haskell@henning-thielemann.de> Maintainer: Henning Thielemann <haskell@henning-thielemann.de> Homepage: http://www.haskell.org/haskellwiki/Exception-Package-URL: http://code.haskell.org/explicit-exception/ Category: Control Stability: Experimental Synopsis: Exceptions which are explicit in the type signature.@@ -37,11 +36,20 @@ However note that freeing resources in case of an error is dangerous and may cause further damage. Tested-With: GHC==6.8.2-Cabal-Version: >=1.2+Cabal-Version: >=1.6 Build-Type: Simple +Source-Repository head+ type: darcs+ location: http://code.haskell.org/explicit-exception/++Source-Repository this+ type: darcs+ location: http://code.haskell.org/explicit-exception/+ tag: 0.1+ Library- Build-Depends: base >= 2, mtl+ Build-Depends: base >= 2, transformers >=0.0 && <0.1 GHC-Options: -Wall Hs-Source-Dirs: src
src/Control/Monad/Exception/Asynchronous.hs view
@@ -1,6 +1,6 @@ {- | Asynchronous exceptions can occur during the construction of a lazy data structure.-They are represent by a lazy data structure itself.+They are represented by a lazy data structure itself. TODO:@@ -34,11 +34,13 @@ If you evaluate the exception part, then the result value is certainly computed completely. -However, we cannot provide functions-that combine several 'Exceptional' values,+However, we cannot provide general 'Monad' functionality due to the very different ways of combining the results of type @a@. It is recommended to process the result value in an application specific way, and after consumption of the result, throw a synchronous exception using 'toSynchronous'.++Maybe in the future we provide a monad instance+which considers subsequent actions as simultaneous processes on a lazy data structure. -} data Exceptional e a = Exceptional {exception :: Maybe e, result :: a}@@ -151,14 +153,14 @@ Exceptional e [a] -> Exceptional e [b] -> Exceptional e [c] zipWith f (Exceptional ea a0) (Exceptional eb b0) = let recourse (a:as) (b:bs) =- fmap (f a b :) (recurseF as bs)+ fmap (f a b :) (recourseF as bs) recourse as _ = Exceptional (case as of [] -> mplus ea eb; _ -> eb) []- recurseF as bs = force $ recourse as bs- in recurseF a0 b0+ recourseF as bs = force $ recourse as bs+ in recourseF a0 b0 -infixr 1 `append`, `continue`+infixr 1 `append`, `continue`, `maybeAbort` {- | This is an example for application specific handling of result values.@@ -186,32 +188,61 @@ Just _ -> Exceptional ea mempty Nothing -> b +maybeAbort ::+ Exceptional e a -> Maybe e -> Exceptional e a+maybeAbort ~(Exceptional ea a) eb =+ Exceptional (mplus ea eb) a + {- | construct Exceptional constructor lazily -} force :: Exceptional e a -> Exceptional e a force ~(Exceptional e a) = Exceptional e a +mapException :: (e0 -> e1) -> Exceptional e0 a -> Exceptional e1 a+mapException f ~(Exceptional e a) = Exceptional (fmap f e) a -{--catch :: Exceptional e0 a -> (e0 -> Exceptional e1 a) -> Exceptional e1 a-catch x handler =- case x of- Success a -> Success a- Exception e -> handler e--}+mapExceptional :: (e0 -> e1) -> (a -> b) -> Exceptional e0 a -> Exceptional e1 b+mapExceptional f g ~(Exceptional e a) = Exceptional (fmap f e) (g a) instance Functor (Exceptional e) where fmap f ~(Exceptional e a) = Exceptional e (f a) -{--Foldable instance would allow to strip off the exception too easily. -instance Foldable (Exceptional e) where+infixr 1 `simultaneousBind`, `simultaneousBindM` +{- |+I consider both actions to process the data simultaneously through lazy evaluation.+If the second one fails too, it must have encountered an exception+in the data that was successfully emitted by the first action,+and thus the exception of the second action is probably earlier. -I like the methods of traversable, but Traversable instance requires Foldable instance+We cannot check in general whether the two exception occur at the same time,+e.g. the second one might occur since the first occured and left an invalid structure.+In this case we should emit the first exception, not the second one.+Because of this I expect that this function is not particularly useful.+Otherwise it could be used as bind operation for a monad instance.+-}+{-# DEPRECATED simultaneousBind, simultaneousBindM "Check whether this function is really what you need. It generates an unreasonable exception when the second exception is caused by the first one." #-}+simultaneousBind :: Exceptional e a -> (a -> Exceptional e b) -> Exceptional e b+simultaneousBind ~(Exceptional mea a) actB =+ let Exceptional meb b = actB a+ in Exceptional (mplus meb mea) b -instance Traversable (Exceptional e) where+simultaneousBindM :: (Monad m) => m (Exceptional e a) -> (a -> m (Exceptional e b)) -> m (Exceptional e b)+simultaneousBindM actA actB =+ do Exceptional mea a <- actA+ Exceptional meb b <- actB a+ return (Exceptional (mplus meb mea) b)+++-- instance Foldable (Exceptional e) where++-- instance Traversable (Exceptional e) where++{- |+@Foldable@ instance would allow to strip off the exception too easily.++I like the methods of @Traversable@, but @Traversable@ instance requires @Foldable@ instance. -} {-# INLINE traverse #-}@@ -236,7 +267,7 @@ {- instance Applicative (Exceptional e) where- pure = Exceptional [] -- [Nothing]?+ pure = pure f <*> x = case f of Exceptional e0 g ->@@ -244,7 +275,7 @@ Exceptional e1 y -> Exceptional (mplus e0 e1) (g y) instance Monad (Exceptional e) where- return = Exceptional [] -- [Nothing]?+ return = pure fail _msg = Exceptional [Just (error "Asynchronous.fail exception")]@@ -254,8 +285,34 @@ Exceptional e0 y -> case f y of Exceptional e1 z -> Exceptional (e0 ++ e1) z+-} +{- |+Consider a file format consisting of a header and a data body.+The header can only be used if is read completely.+Its parsing might stop with an synchronous exception.+The data body can also be used if it is truncated by an exceptional event.+This is expressed by an asynchronous exception.+A loader for this file format can thus fail+by a synchronous and an asynchronous exception.+Surprisingly, both orders of nesting these two kinds of exceptional actions+are equally expressive.+This function converts to the form where the synchronous exception is the outer one.+-}+swapToSynchronousAsynchronous :: Exceptional e0 (Sync.Exceptional e1 a) -> Sync.Exceptional e1 (Exceptional e0 a)+swapToSynchronousAsynchronous ~(Exceptional e0 x) =+ fmap (Exceptional e0) x +swapToAsynchronousSynchronous :: Sync.Exceptional e1 (Exceptional e0 a) -> Exceptional e0 (Sync.Exceptional e1 a)+swapToAsynchronousSynchronous x =+ force $+ case x of+ Sync.Exception e1 -> pure $ Sync.throw e1+ Sync.Success s -> fmap Sync.Success s++++{- -- * Monad transformer newtype ExceptionalT e m a =
src/Control/Monad/Exception/Synchronous.hs view
@@ -5,10 +5,10 @@ module Control.Monad.Exception.Synchronous where import Control.Applicative (Applicative(pure, (<*>)))-import Control.Monad (liftM, )+import Control.Monad (liftM, {- MonadPlus(mzero, mplus), -}) import Control.Monad.Fix (MonadFix, mfix, )-import Control.Monad.Trans (MonadTrans, lift, )-import Control.Monad.Error (ErrorT(ErrorT, runErrorT))+import Control.Monad.Trans (MonadTrans, lift, {- MonadIO(liftIO), -} )+import Control.Monad.Trans.Error (ErrorT(ErrorT, runErrorT)) import Prelude hiding (catch, ) @@ -19,6 +19,8 @@ Like 'Either', but explicitly intended for handling of exceptional results. In contrast to 'Either' we do not support 'fail'. Calling 'fail' in the 'Exceptional' monad is an error.+This way, we do not require that an exception can be derived from a 'String',+yet, we require no constraint on the exception type at all. -} data Exceptional e a = Success a@@ -125,8 +127,16 @@ a = f (unSuccess a) in a +{-+A MonadPlus instance would require another class, say DefaultException,+that provides a default exception used for @mzero@.+In Control.Monad.Error this is handled by the Error class.+Since String is a typical type used for exceptions -+shall there be a DefaultException String instance?+-} + -- * Monad transformer -- | like ErrorT, but ExceptionalT is the better name in order to distinguish from real (programming) errors@@ -257,3 +267,8 @@ instance MonadTrans (ExceptionalT e) where lift m = ExceptionalT $ liftM Success m++{-+instance MonadIO m => MonadIO (ExceptionalT e m) where+ liftIO act = ExceptionalT $ liftIO $ liftM Success act+-}
src/Control/Monad/Label.hs view
@@ -7,6 +7,10 @@ while reading file \'foo.txt\' while loading document \'bar.doc\'\" using the functions in "Control.Monad.Exception.Label".++However, currently I believe that this datatype is unnecessary,+since you can extend exceptions by context information+using 'Control.Monad.Exception.Synchronous.mapException'. -} module Control.Monad.Label where @@ -15,8 +19,8 @@ import Control.Monad (MonadPlus, ap, ) import Control.Monad.Fix (MonadFix) import Control.Monad.Trans (MonadTrans, MonadIO)-import qualified Control.Monad.Reader as Reader-import Control.Monad.Reader (Reader, ReaderT(ReaderT), runReader, runReaderT, )+import qualified Control.Monad.Trans.Reader as Reader+import Control.Monad.Trans.Reader (Reader, ReaderT(ReaderT), runReader, runReaderT, ) import Control.Monad.Instances ()