Last active
December 21, 2015 11:39
-
-
Save singpolyma/6300661 to your computer and use it in GitHub Desktop.
The goal of this module is to provide a way to tell the typesystem "I've caught all the Exceptions, this action is safe"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- A type of IO that does not contain any non-error, synchronous exceptions | |
module SafeIO (SafeIO, unsafeFromIO, fromIO, fromIO', runSafeIO, runEitherIO) where | |
import Control.Applicative (Applicative(..)) | |
import Control.Monad (liftM, ap, (<=<)) | |
import Control.Monad.Fix (MonadFix(..)) | |
import Control.Error (syncIO, mapEitherT, EitherT(..), fmapLT) | |
import Control.Exception (SomeException, Exception, fromException, throwIO) | |
import Control.Monad.IO.Class (liftIO, MonadIO) | |
newtype SafeIO a = SafeIO (IO a) | |
instance Functor SafeIO where | |
fmap = liftM | |
instance Applicative SafeIO where | |
pure = return | |
(<*>) = ap | |
instance Monad SafeIO where | |
return = SafeIO . return | |
(SafeIO x) >>= f = SafeIO (x >>= runSafeIO . f) | |
fail = error "SafeIO does not have exceptions." | |
instance MonadFix SafeIO where | |
mfix f = SafeIO (mfix $ runSafeIO . f) | |
-- | You promise there are no exceptions thrown by this IO action | |
unsafeFromIO :: IO a -> SafeIO a | |
unsafeFromIO = SafeIO | |
-- | Lift IO action and catch any non-error synchronous exceptions | |
fromIO :: IO a -> EitherT SomeException SafeIO a | |
fromIO = mapEitherT unsafeFromIO . syncIO | |
-- | You promise that e covers all exceptions thrown by this IO action | |
-- This function is partial if you lie | |
fromIO' :: (Exception e) => IO a -> EitherT e SafeIO a | |
fromIO' = fmapLT (maybePartial . fromException) . fromIO | |
where | |
maybePartial (Just x) = x | |
maybePartial Nothing = error "SafeIO.fromIO' exception of unspecified type" | |
-- | Re-embed SafeIO into IO | |
runSafeIO :: (MonadIO m) => SafeIO a -> m a | |
runSafeIO (SafeIO io) = liftIO io | |
-- | Re-embed SafeIO with possible exception back into IO | |
runEitherIO :: (MonadIO m, Exception e) => EitherT e SafeIO a -> m a | |
runEitherIO = either (liftIO . throwIO) return <=< runSafeIO . runEitherT |
@Peaker good point about mapEitherT
. I've updated that, made some other cleanups, and added a few more utilities.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
EitherT . f . runEitherT = mapEitherT
Also, GeneralizedNewtypeDeriving to avoid all the instance boilerplate...