-
Notifications
You must be signed in to change notification settings - Fork 70
Description
The onException combinator is essentially implemented as:
step' gst st = do
res <- step gst st `MC.onException` action
case res of
Yield x s -> return $ Yield x s
Skip s -> return $ Skip s
Stop -> return Stop
onException ultimately turns into a catch#, the step is wrapped into a catch# and this code cannot fuse and becomes very inefficient if we are doing exception handling on byte stream. Basically we have to check exceptions on each byte breaking fusion and allocating the constructor for each step. We can see this in the performance of exception combinators on a byte stream, these are the worst benchmarks in the FileSystem.Handle benchmark:
Benchmark default(MiB)
-------------------------------------------------------------------------------------------------------------------- ------------
FileSystem.Handle/o-1-space/copy/read/group-ungroup/UA.unwords . UA.words (Array Char) (1/10) 1576.53
FileSystem.Handle/o-1-space/copy/read/exceptions/S.finallyIO (1/10) 1540.07
FileSystem.Handle/o-1-space/copy/read/exceptions/S.finally (1/10) 1540.07
FileSystem.Handle/o-1-space/copy/read/exceptions/S.handle (1/10) 1540.07
FileSystem.Handle/o-1-space/copy/read/exceptions/S.onException (1/10) 1540.07
FileSystem.Handle/o-1-space/copy/fromToBytes/exceptions/S.bracketIO (1/10) 1379.72
FileSystem.Handle/o-1-space/copy/fromToBytes/exceptions/S.bracket (1/10) 1379.72
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.bracketIO (1/10) 1299.66
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.finallyIO (1/10) 1299.66
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.handle (1/10) 1299.66
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.bracket (1/10) 1140.31
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.finally (1/10) 1140.31
FileSystem.Handle/o-1-space/copy/read/exceptions/UF.onException (1/10) 1140.31
Alternatively, we can have an Error constructor to propagate errors in the stream more efficiently. The error constructor will fuse nicely. At the points where we need to interface with IO and can encounter IO errors we can use catch# and turn that into an Error to propagate through the stream.
Note that we use an Error constructor in parsers for the same purpose, we can have the same in streams as well.