I've seen people with widely varying opinions on whether it's worth using typed error channels either with monad transformer stacks or dedicated bifunctor error handling. Today I found an example that made me more convinced than ever that most everything should extend Throwable.
Specifically, in situations where a lot of your code assumes that the same kinds of errors can happen, especially if they run into LUB issues, it's just noise to have it in every single type signature. You can alias it out but that's an imperfect solution that can hide complexity
I think the whole typed error "debate" really needs to be framed within the context of a properly modeled system where you can isolate errors to the smallest area they appear. I find that having very detailed errors bubbling up all the time can sometimes be a form of coupling.
Your error handling process, regardless of your specific monads (EitherT or something more specialized) depends so much more on how you model your errors and how you handle them, and I think with a good layered error handler setup you solve so many problems. Simple is better.
I think I can sum up my thoughts with:

Do you want the immediate caller to handle the error? Expose it. Otherwise throw it in your Throwable basket and let some dedicated mechanism several layers up handle it. It'll make your life so much easier and it's just as testable.
You can follow @LiquidSloshalot.
Tip: mention @twtextapp on a Twitter thread with the keyword “unroll” to get a link to it.

Latest Threads Unrolled: