The try/catch is basically a different stack, and a single try/catch frame may be many stack frames. I've found that for complex stuff I have to walk up and down my abstractions to understand how exceptions are flowing, and that it isn't easy.
I think it's fundamentally less local to do a conditional jump elsewhere in the code (even if doesn't cross into a different frame) than to return a value and step to the next line, rain or shine. I don't disagree that there's an isomorphism between the approaches (since they're both addressing the same need, surely there should be), but exceptions introduce a new set of rules whereas monadic error handling repurposes the familiar semantics of function calls.
But you're not the first person to mention your second point, so I totally concede that, regardless of whether it is "bad practice," empirically it's a weakness of the approach.
I think it's fundamentally less local to do a conditional jump elsewhere in the code (even if doesn't cross into a different frame) than to return a value and step to the next line, rain or shine. I don't disagree that there's an isomorphism between the approaches (since they're both addressing the same need, surely there should be), but exceptions introduce a new set of rules whereas monadic error handling repurposes the familiar semantics of function calls.
But you're not the first person to mention your second point, so I totally concede that, regardless of whether it is "bad practice," empirically it's a weakness of the approach.