MonadFailDesugaring changes do-notation pattern matching in GHC 8.8+
0 reputation · 08 Mar 2025, 20:34 UTC
Goal
Decide whether a custom monad transformer library should add MonadFail instances when targeting GHC 8.8 and later. In GHC 8.8+, fail moved out of the Monad class into MonadFail, and do-notation pattern-match failures desugar to MonadFail.fail instead of Monad.fail. The MonadFailDesugaring extension is enabled by default, but there is no automatic superclass requirement linking Monad and MonadFail.
Constraints
Code that pattern matches in a do-block may fail to typecheck under GHC 8.8+ unless the relevant type has a MonadFail instance or the extension is disabled. Disabling the extension can restore older behavior but may hide missing instances and defer pattern-match failures to runtime. Libraries supporting both GHC 8.6 and GHC 8.8+ face a compatibility boundary because the desugaring rule changed between releases.
What is the safest way to expose fail for custom monads that previously relied on the Monad method? Should maintainers add MonadFail instances for every transformer, or conditionally disable MonadFailDesugaring for older compiler support? Does disabling the extension preserve pre-8.8 semantics without changing the public API?