composewell / streamly

High performance, concurrent functional programming abstractions
https://streamly.composewell.com
Other
857 stars 64 forks source link

Change the handle and bracket operations for streams and unfolds #530

Open harendra-kumar opened 4 years ago

harendra-kumar commented 4 years ago

Consider the current handle operation:

handle :: (MonadCatch m, Exception e)
    => (e -> Stream m a) -> Stream m a -> Stream m a

It just generates an alternative stream if an exception occurs. This is not powerful enough. We should pass the remaining stream to the exception handler. it can choose to continue executing the remaining stream or to produce an alternative stream. In fact the exception handler could be:

e -> Stream m a -> m (Stream m a)

This will allow stateful generation of stream e.g. if we want to keep retrying the origininal stream until fails a few times, we can maintain the retry count in the monad, when the count reaches zero we can return a nil stream or raise an exception.

We should make similar changes to bracket as well as to the Unfold exception handling routines.

pranaysashank commented 4 years ago

How should bracket's signature change from this since we are using the same function when the stream ends normally or due to an exception?

bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
harendra-kumar commented 4 years ago

I think we can keep the existing/simpler handle and bracket as is and use separate APIs for this generalized use case. We can use something like ghandle for this, we already have a gbracket which can be modified to accommodate this use case.

We need to make sure that the performance of these operations is not affected adversely by this change. There are benchmarks for these in FileSystem.Handle benchmark module.

pranaysashank commented 4 years ago