I’m not sure I actually agree with most of the advice in here, it’s a mix of things I do and don’t agree with. But I’ll take the change to point out a useful library for making a lot of the cost in this post simple: hoist-error.
Code like the awkward
maybe (Left (NotFound argA)) Right $ Map.lookup argA env
Map.lookup argA env <?> NotFound argA
I worked on a small project where we used hoist-error extensively, and being able to align all the operators off to the right cognitively helps a lot; you can ignore all the error code and just read the happy case code.
It makes unifying the various different forms of error really nice too, since it lifts errors from Maybe, Either and ExceptT into the same monad. This allows you to start treating pure functions which return Maybe or Either as if there were just part of your top level error handling monad, avoid the frequent pain of dealing with the choice (often someone else’s choice) to use Maybe here, Either there, etc.