{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{- |
Module defining the type for exception free I/O.
Exceptional results in SIO must be represented by traditional error codes.

If you want to turn an IO action into 'SIO'
you must convert it to @ExceptionalT IOException SIO a@
by 'ioToExceptionalSIO' (or 'Control.Monad.Trans.liftIO')
and then handle the 'IOException's using 'SyncExc.resolveT'.
-}
module System.IO.Straight (
   SIO, sioToIO, ioToExceptionalSIO, unsafeInterleaveSIO,
   ExceptionalT, IOException,
   ) where

import Control.Monad.Exception.Synchronous
   (Exceptional(Success, Exception), ExceptionalT(ExceptionalT), )
import Control.Exception (IOException, try)
import Control.Monad.IO.Class (MonadIO, liftIO, )
import Control.Applicative (Applicative, )

import System.IO.Unsafe (unsafeInterleaveIO, )


{- |
An I/O action of type 'SIO' cannot skip following SIO actions
as a result of exceptional outcomes like \"File not found\".
However an 'error' can well break the program.
-}
newtype SIO a = SIO (IO a) -- {sioToIO :: IO a}
   deriving (a -> SIO b -> SIO a
(a -> b) -> SIO a -> SIO b
(forall a b. (a -> b) -> SIO a -> SIO b)
-> (forall a b. a -> SIO b -> SIO a) -> Functor SIO
forall a b. a -> SIO b -> SIO a
forall a b. (a -> b) -> SIO a -> SIO b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> SIO b -> SIO a
$c<$ :: forall a b. a -> SIO b -> SIO a
fmap :: (a -> b) -> SIO a -> SIO b
$cfmap :: forall a b. (a -> b) -> SIO a -> SIO b
Functor, Functor SIO
a -> SIO a
Functor SIO =>
(forall a. a -> SIO a)
-> (forall a b. SIO (a -> b) -> SIO a -> SIO b)
-> (forall a b c. (a -> b -> c) -> SIO a -> SIO b -> SIO c)
-> (forall a b. SIO a -> SIO b -> SIO b)
-> (forall a b. SIO a -> SIO b -> SIO a)
-> Applicative SIO
SIO a -> SIO b -> SIO b
SIO a -> SIO b -> SIO a
SIO (a -> b) -> SIO a -> SIO b
(a -> b -> c) -> SIO a -> SIO b -> SIO c
forall a. a -> SIO a
forall a b. SIO a -> SIO b -> SIO a
forall a b. SIO a -> SIO b -> SIO b
forall a b. SIO (a -> b) -> SIO a -> SIO b
forall a b c. (a -> b -> c) -> SIO a -> SIO b -> SIO c
forall (f :: * -> *).
Functor f =>
(forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: SIO a -> SIO b -> SIO a
$c<* :: forall a b. SIO a -> SIO b -> SIO a
*> :: SIO a -> SIO b -> SIO b
$c*> :: forall a b. SIO a -> SIO b -> SIO b
liftA2 :: (a -> b -> c) -> SIO a -> SIO b -> SIO c
$cliftA2 :: forall a b c. (a -> b -> c) -> SIO a -> SIO b -> SIO c
<*> :: SIO (a -> b) -> SIO a -> SIO b
$c<*> :: forall a b. SIO (a -> b) -> SIO a -> SIO b
pure :: a -> SIO a
$cpure :: forall a. a -> SIO a
$cp1Applicative :: Functor SIO
Applicative, Applicative SIO
a -> SIO a
Applicative SIO =>
(forall a b. SIO a -> (a -> SIO b) -> SIO b)
-> (forall a b. SIO a -> SIO b -> SIO b)
-> (forall a. a -> SIO a)
-> Monad SIO
SIO a -> (a -> SIO b) -> SIO b
SIO a -> SIO b -> SIO b
forall a. a -> SIO a
forall a b. SIO a -> SIO b -> SIO b
forall a b. SIO a -> (a -> SIO b) -> SIO b
forall (m :: * -> *).
Applicative m =>
(forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: a -> SIO a
$creturn :: forall a. a -> SIO a
>> :: SIO a -> SIO b -> SIO b
$c>> :: forall a b. SIO a -> SIO b -> SIO b
>>= :: SIO a -> (a -> SIO b) -> SIO b
$c>>= :: forall a b. SIO a -> (a -> SIO b) -> SIO b
$cp1Monad :: Applicative SIO
Monad)


sioToIO :: SIO a -> IO a
sioToIO :: SIO a -> IO a
sioToIO (SIO x :: IO a
x) = IO a
x

ioToExceptionalSIO :: IO a -> ExceptionalT IOException SIO a
ioToExceptionalSIO :: IO a -> ExceptionalT IOException SIO a
ioToExceptionalSIO =
   SIO (Exceptional IOException a) -> ExceptionalT IOException SIO a
forall e (m :: * -> *) a. m (Exceptional e a) -> ExceptionalT e m a
ExceptionalT (SIO (Exceptional IOException a) -> ExceptionalT IOException SIO a)
-> (IO a -> SIO (Exceptional IOException a))
-> IO a
-> ExceptionalT IOException SIO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO (Exceptional IOException a) -> SIO (Exceptional IOException a)
forall a. IO a -> SIO a
SIO (IO (Exceptional IOException a) -> SIO (Exceptional IOException a))
-> (IO a -> IO (Exceptional IOException a))
-> IO a
-> SIO (Exceptional IOException a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Either IOException a -> Exceptional IOException a)
-> IO (Either IOException a) -> IO (Exceptional IOException a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((IOException -> Exceptional IOException a)
-> (a -> Exceptional IOException a)
-> Either IOException a
-> Exceptional IOException a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either IOException -> Exceptional IOException a
forall e a. e -> Exceptional e a
Exception a -> Exceptional IOException a
forall e a. a -> Exceptional e a
Success) (IO (Either IOException a) -> IO (Exceptional IOException a))
-> (IO a -> IO (Either IOException a))
-> IO a
-> IO (Exceptional IOException a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO a -> IO (Either IOException a)
forall e a. Exception e => IO a -> IO (Either e a)
try


unsafeInterleaveSIO :: SIO a -> SIO a
unsafeInterleaveSIO :: SIO a -> SIO a
unsafeInterleaveSIO (SIO io :: IO a
io) = IO a -> SIO a
forall a. IO a -> SIO a
SIO (IO a -> SIO a) -> IO a -> SIO a
forall a b. (a -> b) -> a -> b
$ IO a -> IO a
forall a. IO a -> IO a
unsafeInterleaveIO IO a
io

-- helper classes for defining the MonadIO instance of SIO

{- |
Users of the library may define new instances of MonadSIO,
but monads other than SIO may not make the absence of exceptions explicit.
It is important however, that we do not make the method 'toSIO' public,
since this would allow users
the unsafe conversion from @IO@ to @SIO@.

Maybe we should not be so picky about exceptional monads
within exception monad transformers.
A monad like @ExceptionalT e0 (StateT s (Exceptional e1))@
may be useful for distinction
of non-fatal exceptions @e0@ that can maintain the state @s@
and fatal exceptions @e1@ that prevent generation of an updated state.
-}
class Monad m => MonadSIO m where toSIO :: IO a -> m a
instance MonadSIO SIO where toSIO :: IO a -> SIO a
toSIO = IO a -> SIO a
forall a. IO a -> SIO a
SIO

class ContainsIOException e where fromIOException :: IOException -> e
instance ContainsIOException IOException where fromIOException :: IOException -> IOException
fromIOException = IOException -> IOException
forall a. a -> a
id


instance (MonadSIO m, ContainsIOException e) =>
            MonadIO (ExceptionalT e m) where
   liftIO :: IO a -> ExceptionalT e m a
liftIO =
      m (Exceptional e a) -> ExceptionalT e m a
forall e (m :: * -> *) a. m (Exceptional e a) -> ExceptionalT e m a
ExceptionalT (m (Exceptional e a) -> ExceptionalT e m a)
-> (IO a -> m (Exceptional e a)) -> IO a -> ExceptionalT e m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO (Exceptional e a) -> m (Exceptional e a)
forall (m :: * -> *) a. MonadSIO m => IO a -> m a
toSIO (IO (Exceptional e a) -> m (Exceptional e a))
-> (IO a -> IO (Exceptional e a)) -> IO a -> m (Exceptional e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
      (Either IOException a -> Exceptional e a)
-> IO (Either IOException a) -> IO (Exceptional e a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((IOException -> Exceptional e a)
-> (a -> Exceptional e a)
-> Either IOException a
-> Exceptional e a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (e -> Exceptional e a
forall e a. e -> Exceptional e a
Exception (e -> Exceptional e a)
-> (IOException -> e) -> IOException -> Exceptional e a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IOException -> e
forall e. ContainsIOException e => IOException -> e
fromIOException) a -> Exceptional e a
forall e a. a -> Exceptional e a
Success) (IO (Either IOException a) -> IO (Exceptional e a))
-> (IO a -> IO (Either IOException a))
-> IO a
-> IO (Exceptional e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IO a -> IO (Either IOException a)
forall e a. Exception e => IO a -> IO (Either e a)
try