@@ -215,7 +215,7 @@ module Streamly.Streams.StreamD
215 , zipWith
215 , zipWith
216 , zipWithM
216 , zipWithM
218− -- * Comparisions
218+ -- * Comparisons
@@ -225,9 +225,19 @@ module Streamly.Streams.StreamD
226 -- * Transformation comprehensions
226 -- * Transformation comprehensions
239+import Control.Exception (Exception)
240+import Control.Monad.Catch (MonadCatch)
231import Control.Monad.IO.Class (MonadIO(..))
241import Control.Monad.IO.Class (MonadIO(..))
232import Control.Monad.Trans (MonadTrans(lift))
242import Control.Monad.Trans (MonadTrans(lift))
233import Data.Word (Word32)
243import Data.Word (Word32)
@@ -243,6 +253,8 @@ import Prelude
243 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
253 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
256+import qualified Control.Monad.Catch as MC
246import Streamly.Mem.Array.Types (Array(..))
258import Streamly.Mem.Array.Types (Array(..))
247import Streamly.Fold.Types (Fold(..))
259import Streamly.Fold.Types (Fold(..))
248import Streamly.SVar (MonadAsync, defState, adaptState)
260import Streamly.SVar (MonadAsync, defState, adaptState)
@@ -1663,6 +1675,100 @@ mapM_ m = drain . mapM m
1663fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
1675fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
1664fromStreamD = K.fromStream . toStreamK
1676fromStreamD = K.fromStream . toStreamK
1678+------------------------------------------------------------------------------
1680+------------------------------------------------------------------------------
1682+-- | Run a side effect before the stream yields its first element.
1683+before :: Monad m => m b -> Stream m a -> Stream m a
1684+before action (Stream step state) = Stream step' Nothing
1688+ {-# INLINE_LATE step' #-}
1689+ step' _ Nothing = action >> return (Skip (Just state))
1691+ step' gst (Just st) = do
1694+ Yield x s -> return $ Yield x (Just s)
1695+ Skip s -> return $ Skip (Just s)
1696+ Stop -> return Stop
1698+-- | Run a side effect whenever the stream stops normally.
1699+after :: Monad m => m b -> Stream m a -> Stream m a
1700+after action (Stream step state) = Stream step' state
1704+ {-# INLINE_LATE step' #-}
1708+ Yield x s -> return $ Yield x s
1709+ Skip s -> return $ Skip s
1710+ Stop -> action >> return Stop
1712+-- | Run a side effect whenever the stream aborts due to an exception. The
1713+-- exception is not caught simply rethrown.
1714+onException :: MonadCatch m => m b -> Stream m a -> Stream m a
1715+onException action (Stream step state) = Stream step' state
1719+ {-# INLINE_LATE step' #-}
1721+ res <- step gst st `MC.onException` action
1723+ Yield x s -> return $ Yield x s
1724+ Skip s -> return $ Skip s
1725+ Stop -> action >> return Stop
1727+-- XXX check if a monolithic implementation would be more performant.
1729+-- | Run a side effect whenever the stream stops normally or aborts due to an
1731+finally :: MonadCatch m => m b -> Stream m a -> Stream m a
1732+finally action xs = after action $ onException action xs
1734+-- | Run the first action before the stream starts and remember its output,
1735+-- generate a stream using the output, run the second action using the
1736+-- remembered value as an argument whenever the stream ends normally or due to
1738+bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
1739+bracket bef aft bet = Stream step' Nothing
1743+ {-# INLINE_LATE step' #-}
1744+ step' _ Nothing = bef >>= \x -> return (Skip (Just (bet x, x)))
1746+ step' gst (Just (Stream step state, v)) = do
1747+ res <- step gst state `MC.onException` aft v
1749+ Yield x s -> return $ Yield x (Just (Stream step s, v))
1750+ Skip s -> return $ Skip (Just (Stream step s, v))
1751+ Stop -> aft v >> return Stop
1753+-- | When evaluating a stream if an exception occurs, stream evaluation aborts
1754+-- and the specified exception handler is run with the exception as argument.
1755+handle :: (MonadCatch m, Exception e) => (e -> m a) -> Stream m a -> Stream m a
1756+handle f (Stream step state) = Stream step' (Just state)
1760+ {-# INLINE_LATE step' #-}
1761+ step' gst (Just st) = do
1762+ res <- MC.try $ step gst st
1764+ Left e -> f e >>= \x -> return (Yield x Nothing)
1765+ Right r -> case r of
1766+ Yield x s -> return $ Yield x (Just s)
1767+ Skip s -> return $ Skip (Just s)
1768+ Stop -> return Stop
1770+ step' _ Nothing = return Stop
1666------------------------------------------------------------------------------
1772------------------------------------------------------------------------------
1667-- Transformation by Folding (Scans)
1773-- Transformation by Folding (Scans)
1668------------------------------------------------------------------------------
1774------------------------------------------------------------------------------