module Bio.Iteratee.Base (
Stream (..)
,StreamStatus (..)
,module Bio.Iteratee.Exception
,Iteratee (..)
,run
,tryRun
,ilift
,ifold
,idone
,icont
,liftI
,idoneM
,icontM
,setEOF
,NullPoint(..)
,Nullable(..)
)
where
import Bio.Iteratee.Exception
import Bio.Prelude
import Control.Monad.Catch as CIO
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import qualified Control.Exception as E
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
class NullPoint c where emptyP :: c
instance NullPoint (Endo a) where emptyP = Endo id
instance NullPoint [a] where emptyP = []
instance NullPoint B.ByteString where emptyP = B.empty
instance NullPoint L.ByteString where emptyP = L.empty
class NullPoint c => Nullable c where nullC :: c -> Bool
instance Nullable [a] where nullC [] = True ; nullC _ = False
instance Nullable B.ByteString where nullC = B.null
instance Nullable L.ByteString where nullC = L.null
data Stream c = EOF (Maybe SomeException) | Chunk c
deriving (Show, Typeable)
instance (Eq c) => Eq (Stream c) where
(Chunk c1) == (Chunk c2) = c1 == c2
(EOF Nothing) == (EOF Nothing) = True
(EOF (Just e1)) == (EOF (Just e2)) = typeOf e1 == typeOf e2
_ == _ = False
instance Semigroup c => Semigroup (Stream c) where
EOF mErr <> _ = EOF mErr
_ <> EOF mErr = EOF mErr
Chunk s1 <> Chunk s2 = Chunk (s1 <> s2)
instance Monoid c => Monoid (Stream c) where
mempty = Chunk mempty
EOF mErr `mappend` _ = EOF mErr
_ `mappend` EOF mErr = EOF mErr
Chunk s1 `mappend` Chunk s2 = Chunk (mappend s1 s2)
instance Functor Stream where
fmap f (Chunk xs) = Chunk $ f xs
fmap _ (EOF mErr) = EOF mErr
data StreamStatus =
DataRemaining
| EofNoError
| EofError SomeException
deriving (Show, Typeable)
setEOF :: Stream c -> SomeException
setEOF (EOF (Just e)) = e
setEOF _ = toException EofException
newtype Iteratee s m a = Iteratee{ runIter :: forall r.
(a -> Stream s -> m r) ->
((Stream s -> Iteratee s m a) -> Maybe SomeException -> m r) ->
m r}
idone :: a -> Stream s -> Iteratee s m a
idone a s = Iteratee $ \onDone _ -> onDone a s
icont :: (Stream s -> Iteratee s m a) -> Maybe SomeException -> Iteratee s m a
icont k e = Iteratee $ \_ onCont -> onCont k e
liftI :: (Stream s -> Iteratee s m a) -> Iteratee s m a
liftI k = Iteratee $ \_ onCont -> onCont k Nothing
idoneM :: Monad m => a -> Stream s -> m (Iteratee s m a)
idoneM x str = return $ Iteratee $ \onDone _ -> onDone x str
icontM
:: Monad m =>
(Stream s -> Iteratee s m a)
-> Maybe SomeException
-> m (Iteratee s m a)
icontM k e = return $ Iteratee $ \_ onCont -> onCont k e
instance (Functor m) => Functor (Iteratee s m) where
fmap f m = Iteratee $ \onDone onCont ->
let od = onDone . f
oc = onCont . (fmap f .)
in runIter m od oc
instance (Functor m, Monad m, Nullable s) => Applicative (Iteratee s m) where
pure x = idone x (Chunk emptyP)
m <*> a = m >>= flip fmap a
instance (Monad m, Nullable s) => Monad (Iteratee s m) where
return x = Iteratee $ \onDone _ -> onDone x (Chunk emptyP)
(>>=) = bindIteratee
bindIteratee :: Nullable s
=> Iteratee s m a
-> (a -> Iteratee s m b)
-> Iteratee s m b
bindIteratee m f = Iteratee $ \onDone onCont ->
let m_done a (Chunk s)
| nullC s = runIter (f a) onDone onCont
m_done a stream = runIter (f a) (const . flip onDone stream) f_cont
where f_cont k Nothing = runIter (k stream) onDone onCont
f_cont k e = onCont k e
in runIter m m_done (onCont . (flip bindIteratee f .))
instance NullPoint s => MonadTrans (Iteratee s) where
lift m = Iteratee $ \onDone _ -> m >>= flip onDone (Chunk emptyP)
instance (MonadIO m, Nullable s, NullPoint s) => MonadIO (Iteratee s m) where
liftIO = lift . liftIO
instance (MonadThrow m, Nullable s, NullPoint s) =>
MonadThrow (Iteratee s m) where
throwM e = lift $ CIO.throwM e
instance (MonadCatch m, Nullable s, NullPoint s) =>
MonadCatch (Iteratee s m) where
m `catch` f = Iteratee $ \od oc -> runIter m od oc `CIO.catch` (\e -> runIter (f e) od oc)
instance (MonadMask m, Nullable s, NullPoint s) => MonadMask (Iteratee s m) where
mask q = Iteratee $ \od oc -> CIO.mask $ \u -> runIter (q $ ilift u) od oc
uninterruptibleMask q = Iteratee $ \od oc -> CIO.uninterruptibleMask $ \u -> runIter (q $ ilift u) od oc
run :: Monad m => Iteratee s m a -> m a
run iter = runIter iter onDone onCont
where
onDone x _ = return x
onCont k Nothing = runIter (k (EOF Nothing)) onDone onCont'
onCont _ (Just e) = E.throw e
onCont' _ Nothing = E.throw EofException
onCont' _ (Just e) = E.throw e
tryRun :: (Exception e, Monad m) => Iteratee s m a -> m (Either e a)
tryRun iter = runIter iter onDone onCont
where
onDone x _ = return $ Right x
onCont k Nothing = runIter (k (EOF Nothing)) onDone onCont'
onCont _ (Just e) = return $ maybeExc e
onCont' _ Nothing = return $ maybeExc (toException EofException)
onCont' _ (Just e) = return $ maybeExc e
maybeExc e = maybe (Left (E.throw e)) Left (fromException e)
ilift ::
(Monad m, Monad n)
=> (forall r. m r -> n r)
-> Iteratee s m a
-> Iteratee s n a
ilift f i = Iteratee $ \od oc ->
let onDone a str = return $ Left (a,str)
onCont k mErr = return $ Right (ilift f . k, mErr)
in f (runIter i onDone onCont) >>= either (uncurry od) (uncurry oc)
ifold :: (Monad m, Monad n) => (forall r. m r -> acc -> n (r, acc))
-> acc -> Iteratee s m a -> Iteratee s n (a, acc)
ifold f acc i = Iteratee $ \ od oc -> do
(r, acc') <- flip f acc $
runIter i (curry $ return . Left) (curry $ return . Right)
either (uncurry (od . flip (,) acc'))
(uncurry (oc . (ifold f acc .))) r