I\'m currently writing some pipes-core/attoparsec plumbing for a small project of mine. I want each parser to give a pipe that awaits ByteString
input to the pa
If I may, I think I can help organize everybody's thoughts on this by describing the choice like so. You either:
Layer Pipe
within an EitherT
/ErrorT
:
EitherT e (Pipe a b m) r
Layer Pipe
outside of an EitherT
/ErrorT
:
Pipe a b (EitherT e m) r
You want the former approach, which also has the nice property that you can make it an instance of MonadError (if that is your thing).
To understand the difference between the two approaches, the second one throws errors at the level of the entire pipeline. The first one permits error handling at the granularity of individual pipes and correctly handles composed pipes.
Now for some code. I'll use EitherT if you don't mind, since I'm more comfortable with it:
import Control.Error
import Control.Pipe
type PipeE e a b m r = EitherT e (Pipe a b m) r
runPipeE = runPipe . runEitherT
p1 <?< p2 = EitherT (runEitherT p1 <+< runEitherT p2)
Then just use catchT and throwT within a PipeE to your heart's content.
This approach has another advantage, which is that you can selectively apply it to certain segments of the pipeline, but then you are responsible for dealing with the potential exceptional value before composing it with other pipes. You could use that flexibility to use exceptional values of different types for different stages of the pipeline or to not use it at all for stages that can't fail and avoid the overhead of error-checking those stages.