currently, in order to handle exceptions in different resources differently, I'm doing something akin to this:
def resource1: Resource[F, Service1[F]]
def resource2: Resource[F, Service2[F]]
def resource3: Resource[F, Service3[F]]
def runProgram(Service1[F], Service2[F], Service3[F]): F[ExitCode]
resource1.use(r1 =>
resource2.use(r2 =>
resource3.use(r3 =>
runProgram(r1, r2, r3) //handles its own exceptions
).handleErrorWith(e => logger.error(e)("error during service 3 setup").as(ExitCode.Error))
).handleErrorWith(e => logger.error(e)("error during service 2 setup").as(ExitCode.Error))
).handleErrorWith(e => logger.error(e)("error during service 1 setup").as(ExitCode.Error))
This works fine, but it's not hard to see why it doesn't scale well. I've tried using .attempt directly on the Resource but it doesn't seem to catch anything. I can imagine figuring out a way to do it in a for comprehension if I use allocate, but I'd rather not If I don't need to. Anyone know a better way?
Edit
So it turns out that the resources I was creating were starting up their own threadpool and doing a lot of impure async processing which caused unstable behavior. It seems that attempt, onError, handleErrorWith, and friends should work just fine for resources that are constructed well.
I am unsure why my nested code seemed to be able to get around the instability, while the other MonadError based solution didn't. Without sufficient evidence to the contrary I am forced to conclude that it must just be lucky timing.