But in most cases exceptions are the slow path. There's a few languages that use them heavily as what one could think of as a form of out of band dynamic typing for function results, and which have specific support for fast exceptions to support that. In most, exception handling is much heavier weight than the usual code path, and needs to be because it needs to unwind the stack and free up allocated temporaries (presumably that last doesn't apply to Haskell. The pattern match stack would need the same unwind cleanup though) and otherwise clean up any unfinished business that will never finish as a result, as it propagates looking for a handler. Since they also usually contain an execution trace, that makes them even heavier (again, ghc does this differently --- one could argue worse, since the price is paid by all functions with HasCallStack context. I think only Icon of other languages I know penalize the normal path the same way --- because it's also how &fail / retry semantics is implemented).
...looking at that again, maybe I'm too tired to do this. Hopefully you can unpack that concept hairball....