{-# LANGUAGE Rank2Types, FlexibleContexts #-}
module Bio.Iteratee.Iteratee (
EnumerateeHandler
,throwErr
,throwRecoverableErr
,checkErr
,skipToEof
,isStreamFinished
,mBind
,mBind_
,ioBind
,ioBind_
,MonadBracketIO(..)
,mapChunksM_
,foldChunksM
,getChunk
,getChunks
,mapChunks
,mapChunksM
,convStream
,unfoldConvStream
,unfoldConvStreamCheck
,joinI
,joinIM
,Enumerator
,Enumeratee
,enumChunk
,enumEof
,enumErr
,enumPure1Chunk
,enumList
,enumCheckIfDone
,enumFromCallback
,enumFromCallbackCatch
,eneeCheckIfDone
,eneeCheckIfDoneHandle
,eneeCheckIfDoneIgnore
,eneeCheckIfDonePass
,mergeEnums
,($=)
,(=$)
,(><>)
,(<><)
,seek
,module Bio.Iteratee.Base
)
where
import Bio.Iteratee.Base
import Bio.Prelude hiding (loop)
import Control.Monad.Catch as CIO
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Trans.Class (MonadTrans(..))
excDivergent :: SomeException
excDivergent = toException DivergentException
throwErr :: SomeException -> Iteratee s m a
throwErr e = icont (const (throwErr e)) (Just e)
throwRecoverableErr ::
SomeException
-> (Stream s -> Iteratee s m a)
-> Iteratee s m a
throwRecoverableErr e i = icont i (Just e)
checkErr ::
(NullPoint s) =>
Iteratee s m a
-> Iteratee s m (Either SomeException a)
checkErr iter = Iteratee $ \onDone onCont ->
let od = onDone . Right
oc k Nothing = onCont (checkErr . k) Nothing
oc _ (Just e) = onDone (Left e) (Chunk emptyP)
in runIter iter od oc
isStreamFinished :: (Nullable s) => Iteratee s m (Maybe SomeException)
isStreamFinished = liftI check
where
check s@(Chunk xs)
| nullC xs = isStreamFinished
| otherwise = idone Nothing s
check s@(EOF e) = idone (Just $ fromMaybe (toException EofException) e) s
{-# INLINE isStreamFinished #-}
skipToEof :: Iteratee s m ()
skipToEof = icont check Nothing
where
check (Chunk _) = skipToEof
check s = idone () s
seek :: Nullable s => FileOffset -> Iteratee s m ()
seek o = throwRecoverableErr (toException $ SeekException o) (idone ())
mapChunksM_ :: (Monad m, Nullable s) => (s -> m b) -> Iteratee s m ()
mapChunksM_ f = liftI step
where
step (Chunk xs)
| nullC xs = liftI step
| otherwise = lift (f xs) >> liftI step
step s@(EOF _) = idone () s
{-# INLINE mapChunksM_ #-}
foldChunksM :: (Monad m, Nullable s) => (a -> s -> m a) -> a -> Iteratee s m a
foldChunksM f = liftI . go
where
go a (Chunk c) = lift (f a c) >>= liftI . go
go a e = idone a e
{-# INLINE foldChunksM #-}
getChunk :: Nullable s => Iteratee s m s
getChunk = liftI step
where
step (Chunk xs)
| nullC xs = liftI step
| otherwise = idone xs $ Chunk emptyP
step (EOF Nothing) = throwErr $ toException EofException
step (EOF (Just e)) = throwErr e
{-# INLINE getChunk #-}
getChunks :: (Nullable s) => Iteratee s m [s]
getChunks = liftI (step id)
where
step acc (Chunk xs)
| nullC xs = liftI (step acc)
| otherwise = liftI (step $ acc . (xs:))
step acc stream = idone (acc []) stream
{-# INLINE getChunks #-}
type Enumeratee sFrom sTo m a =
Iteratee sTo m a
-> Iteratee sFrom m (Iteratee sTo m a)
{-# INLINE eneeCheckIfDone #-}
eneeCheckIfDone ::
(Monad m, NullPoint elo) =>
((Stream eli -> Iteratee eli m a) -> Iteratee elo m (Iteratee eli m a))
-> Enumeratee elo eli m a
eneeCheckIfDone f = eneeCheckIfDonePass f'
where
f' k Nothing = f k
f' k (Just e) = throwRecoverableErr e (\s -> joinIM $ enumChunk s $ eneeCheckIfDone f (liftI k))
type EnumerateeHandler eli elo m a =
(Stream eli -> Iteratee eli m a)
-> SomeException
-> Iteratee elo m (Iteratee eli m a)
eneeCheckIfDoneHandle
:: (NullPoint elo)
=> EnumerateeHandler eli elo m a
-> ((Stream eli -> Iteratee eli m a)
-> Maybe SomeException
-> Iteratee elo m (Iteratee eli m a)
)
-> Enumeratee elo eli m a
eneeCheckIfDoneHandle h f inner = Iteratee $ \od oc ->
let onDone x s = od (idone x s) (Chunk emptyP)
onCont k Nothing = runIter (f k Nothing) od oc
onCont k (Just e) = runIter (h k e) od oc
in runIter inner onDone onCont
{-# INLINABLE eneeCheckIfDoneHandle #-}
eneeCheckIfDonePass
:: (NullPoint elo)
=> ((Stream eli -> Iteratee eli m a)
-> Maybe SomeException
-> Iteratee elo m (Iteratee eli m a)
)
-> Enumeratee elo eli m a
eneeCheckIfDonePass f = eneeCheckIfDoneHandle (\k e -> f k (Just e)) f
{-# INLINABLE eneeCheckIfDonePass #-}
eneeCheckIfDoneIgnore
:: (NullPoint elo)
=> ((Stream eli -> Iteratee eli m a)
-> Maybe SomeException
-> Iteratee elo m (Iteratee eli m a)
)
-> Enumeratee elo eli m a
eneeCheckIfDoneIgnore f = eneeCheckIfDoneHandle (\k _ -> f k Nothing) f
{-# INLINE mBind #-}
infixl 1 `mBind`
mBind :: Monad m => m a -> (a -> Iteratee s m b) -> Iteratee s m b
mBind m f = Iteratee $ \onDone onCont -> m >>= \a -> runIter (f a) onDone onCont
{-# INLINE mBind_ #-}
infixl 1 `mBind_`
mBind_ :: Monad m => m a -> Iteratee s m b -> Iteratee s m b
mBind_ m b = Iteratee $ \onDone onCont -> m >> runIter b onDone onCont
{-# INLINE ioBind #-}
infixl 1 `ioBind`
ioBind :: MonadIO m => IO a -> (a -> Iteratee s m b) -> Iteratee s m b
ioBind m f = Iteratee $ \onDone onCont -> liftIO m >>= \a -> runIter (f a) onDone onCont
{-# INLINE ioBind_ #-}
infixl 1 `ioBind_`
ioBind_ :: MonadIO m => IO a -> Iteratee s m b -> Iteratee s m b
ioBind_ m b = Iteratee $ \onDone onCont -> liftIO m >> runIter b onDone onCont
class MonadIO m => MonadBracketIO m where
bracketIO :: IO a -> (a -> IO b) -> (a -> m c) -> m c
instance MonadBracketIO IO where
{-# INLINE bracketIO #-}
bracketIO = CIO.bracket
instance (MonadBracketIO m, Nullable s) => MonadBracketIO (Iteratee s m) where
{-# INLINE bracketIO #-}
bracketIO acquire release use =
Iteratee $ \od oc -> bracketIO acquire release (\h -> runIter (use h) od oc)
mapChunks :: (NullPoint s) => (s -> s') -> Enumeratee s s' m a
mapChunks f = eneeCheckIfDonePass (icont . step)
where
step k (Chunk xs) = eneeCheckIfDonePass (icont . step) . k . Chunk $ f xs
step k str@(EOF mErr) = idone (k $ EOF mErr) str
{-# INLINE mapChunks #-}
mapChunksM :: (Monad m, NullPoint s) => (s -> m s') -> Enumeratee s s' m a
mapChunksM f = eneeCheckIfDonePass (icont . step)
where
step k (Chunk xs) = f xs `mBind` eneeCheckIfDonePass (icont . step) . k . Chunk
step k str = idone (liftI k) str
{-# INLINE mapChunksM #-}
convStream ::
(Monad m, Nullable s) =>
Iteratee s m s'
-> Enumeratee s s' m a
convStream fi = eneeCheckIfDonePass check
where
check k (Just e) = throwRecoverableErr e (idone ()) >> check k Nothing
check k _ = isStreamFinished >>= maybe (step k) (idone (liftI k) . EOF . Just)
step k = fi >>= eneeCheckIfDonePass check . k . Chunk
{-# INLINABLE convStream #-}
unfoldConvStream ::
(Monad m, Nullable s) =>
(acc -> Iteratee s m (acc, s'))
-> acc
-> Enumeratee s s' m a
unfoldConvStream f acc0 = eneeCheckIfDonePass (check acc0)
where
check acc k (Just e) = throwRecoverableErr e (idone ()) >> check acc k Nothing
check acc k _ = isStreamFinished >>=
maybe (step acc k) (idone (liftI k) . EOF . Just)
step acc k = f acc >>= \(acc', s') ->
eneeCheckIfDonePass (check acc') . k . Chunk $ s'
{-# INLINABLE unfoldConvStream #-}
unfoldConvStreamCheck
:: (Monad m, Nullable elo)
=> (((Stream eli -> Iteratee eli m a)
-> Maybe SomeException
-> Iteratee elo m (Iteratee eli m a)
)
-> Enumeratee elo eli m a
)
-> (acc -> Iteratee elo m (acc, eli))
-> acc
-> Enumeratee elo eli m a
unfoldConvStreamCheck checkDone f acc0 = checkDone (check acc0)
where
check acc k mX = isStreamFinished >>=
maybe (step acc k mX) (idone (icont k mX) . EOF . Just)
step acc k Nothing = f acc >>= \(acc', s') ->
(checkDone (check acc') . k $ Chunk s')
step acc k (Just ex) = throwRecoverableErr ex $ \str' ->
let i = f acc >>= \(acc', s') ->
(checkDone (check acc') . k $ Chunk s')
in joinIM $ enumChunk str' i
{-# INLINABLE unfoldConvStreamCheck #-}
joinI ::
(Monad m, Nullable s) =>
Iteratee s m (Iteratee s' m a)
-> Iteratee s m a
joinI = (>>=
\inner -> Iteratee $ \od oc ->
let onDone x _ = od x (Chunk emptyP)
onCont k Nothing = runIter (k (EOF Nothing)) onDone onCont'
onCont _ (Just e) = runIter (throwErr e) od oc
onCont' _ e = runIter (throwErr (fromMaybe excDivergent e)) od oc
in runIter inner onDone onCont)
{-# INLINE joinI #-}
joinIM :: (Monad m) => m (Iteratee s m a) -> Iteratee s m a
joinIM mIter = Iteratee $ \od oc -> mIter >>= \iter -> runIter iter od oc
type Enumerator s m a = Iteratee s m a -> m (Iteratee s m a)
enumChunk :: (Monad m) => Stream s -> Enumerator s m a
enumChunk (Chunk xs) = enumPure1Chunk xs
enumChunk (EOF Nothing) = enumEof
enumChunk (EOF (Just e)) = enumErr e
enumEof :: (Monad m) => Enumerator s m a
enumEof iter = runIter iter onDone onCont
where
onDone x _str = return $ idone x (EOF Nothing)
onCont k Nothing = runIter (k (EOF Nothing)) onDone onCont'
onCont k e = return $ icont k e
onCont' _ Nothing = return $ throwErr excDivergent
onCont' k e = return $ icont k e
enumErr :: (Exception e, Monad m) => e -> Enumerator s m a
enumErr e iter = runIter iter onDone onCont
where
onDone x _ = return $ idone x (EOF . Just $ toException e)
onCont k Nothing = runIter (k (EOF (Just (toException e)))) onDone onCont'
onCont k e' = return $ icont k e'
onCont' _ Nothing = return $ throwErr excDivergent
onCont' k e' = return $ icont k e'
infixr 0 =$
(=$)
:: (Nullable s, Monad m)
=> Enumeratee s s' m a
-> Iteratee s' m a
-> Iteratee s m a
(=$) = (.) joinI
infixl 1 $=
($=)
:: Monad m
=> (forall a. Enumerator s m a)
-> Enumeratee s s' m b
-> Enumerator s' m b
($=) enum enee iter = enum (enee iter) >>= run
(><>) ::
(Nullable s1, Monad m)
=> (forall x . Enumeratee s1 s2 m x)
-> Enumeratee s2 s3 m a
-> Enumeratee s1 s3 m a
f ><> g = joinI . f . g
(<><) ::
(Nullable s1, Monad m)
=> Enumeratee s2 s3 m a
-> (forall x. Enumeratee s1 s2 m x)
-> Enumeratee s1 s3 m a
f <>< g = joinI . g . f
mergeEnums ::
(Nullable s2, Nullable s1, Monad m)
=> Enumerator s1 m a
-> Enumerator s2 (Iteratee s1 m) a
-> Enumeratee s2 s1 (Iteratee s1 m) a
-> Enumerator s1 m a
mergeEnums e1 e2 etee i = e1 $ e2 (joinI . etee $ ilift lift i) >>= run
{-# INLINE mergeEnums #-}
enumPure1Chunk :: (Monad m) => s -> Enumerator s m a
enumPure1Chunk str iter = runIter iter idoneM onCont
where
onCont k Nothing = return $ k $ Chunk str
onCont k e = return $ icont k e
enumList :: (Monad m) => [s] -> Enumerator s m a
enumList = go
where
go [] i = return i
go xs' i = runIter i idoneM (onCont xs')
where
onCont (x:xs) k Nothing = go xs . k $ Chunk x
onCont _ _ (Just e) = return $ throwErr e
onCont _ k Nothing = return $ icont k Nothing
{-# INLINABLE enumList #-}
enumCheckIfDone :: (Monad m) => Iteratee s m a -> m (Bool, Iteratee s m a)
enumCheckIfDone iter = runIter iter onDone onCont
where
onDone x str = return (True, idone x str)
onCont k e = return (False, icont k e)
{-# INLINE enumCheckIfDone #-}
enumFromCallback ::
(Monad m, NullPoint s) =>
(st -> m (Either SomeException ((Bool, st), s)))
-> st
-> Enumerator s m a
enumFromCallback c = loop
where
loop st iter = runIter iter idoneM (onCont st)
check k (True, st') = loop st' . k . Chunk
check k (False,_st') = return . k . Chunk
onCont st k Nothing = c st >>=
either (return . k . EOF . Just) (uncurry (check k))
onCont _st k j = return (icont k j)
enumFromCallbackCatch
:: (IException e, Monad m, NullPoint s)
=> (st -> m (Either SomeException ((Bool, st), s)))
-> (e -> m (Maybe EnumException))
-> st
-> Enumerator s m a
enumFromCallbackCatch c handler = loop
where
loop st iter = runIter iter idoneM (onCont st)
check k (True, st') = loop st' . k . Chunk
check k (False,_st') = return . k . Chunk
onCont st k Nothing = c st >>=
either (return . k . EOF . Just) (uncurry (check k))
onCont st k j@(Just e) = case fromException e of
Just e' -> handler e' >>=
maybe (loop st . k $ Chunk emptyP)
(return . icont k . Just) . fmap toException
Nothing -> return (icont k j)
{-# INLINE enumFromCallbackCatch #-}