The classic ADT of errors approach tends to be introduce a central choke point for all modifications. I haven't found it to be useful in practice in any system I've used, and have always migrated away from that to one of the first two. However, I've usually been working in test suites, which have a more ephemeral quality to errors, rather than GHC, where the errors may legitimately want to be distinguished.
Some systems have a requirement to give numbers to error messages, which an ADT approach supports much better.
For things like suggestions from errors, either an ADT or dynamic approach works much better. At the moment suggestions are generated by looking at the textual output, which is grim.
FWIW, for GHC, I'd lean in the direction of an ADT, with a function to generate custom pretty printing with format/holes etc.
Some notes:
FWIW, for GHC, I'd lean in the direction of an ADT, with a function to generate custom pretty printing with format/holes etc.