I would like to propose either to the Haskell foundation or to the community at large to make a universal way to print any Haskell value in any context, anywhere.
As a long-term Haskell consultant, I have seen that on almost all “real world” Haskell codebases:
- There are large nested data types.
- It is very often the case that there is no Show instance for these types, at all. (Look at, for example, the GHC codebase. There is only a pretty printer, which omits information and generally makes it impossible to see the real values.)
- It’s almost always impractical to go and derive Show for all the instances of all the nested types.
Approaches that fall short:
Showinstances often omit information. Or they give a non-source-code view (e.g.
Dayprints as 2012-02-01).
- Approaches that look at the heap (ghc-heap-view) show a view of the data completely alien to the normal way of dealing with Haskell values.
- The GHCi debugger is hard to use and also does not provide fidelty with the original way a data structure was formed in code. In fact, I’ve never seen a single codebase anywhere that had ever been ran via the debugger.
- Class-based approaches don’t work:
Data``/Lift– anything that depends on the availability of a class is inherently not usable because it requires cooperation from a developer in the past to ensure these classes are implemented (correctly) for all types.
Liftclass is sort of related: with
Lift, you can go from a normal Haskell value to an AST. In some ways,
Liftis a more faithful implementation of the not-really-followed rule-of-thumb that
Showshould yield valid source code.
None of them deal with the fact that there are usually a couple valid presentations – the structural representation of the object, and the abstract data type version. Both are valid, and providing only one deprives the programmer.
Some prior work by me:
- In The printer Haskell deserves (2014), I explored a simple way to use
Data.Data, with mentioned downsides.
- In the present package I explored a way to derive such a printer for all types using template-haskell, requiring no prior instances, generating a lazy AST, capable of being shown as Show-like or JSON or whatever, and able to penetrate abstract data types like ByteString or Map to give alternative presentations as either e.g. a pointer+len+offset, or “like a string”, or as a tree in the case of Map. You could also provide additional presentations for a known type by implementing an instance of a class for it–as an optional user-extendable enhancement, not as a base. It only depended on base and template-haskell, so it was easy to include in any project. I got stuck on higher kinded types, as an implementation detail and overall I’m not sure whether it’s the right long-term approach.
GADTs, data families, and other more exotic types present a challenge, but even a best-effort would be valuable there.
What I woud like to have, either as a compiler plugin, an extension, template haskell, or some other means is something that lets me say: I have an expression or a type of expression, now give me a function that can print it in a uniform way. I should never be in the middle of working on a codebase, have a value whose type I know, and be stopped dead in my tracks when I want to see what’s inside it.
Because it’s such a fundamental problem, I’m inclined to lean towards even a language extension that would “standardise” it.
If you are in agreement that this is a problem to be solved, and you are:
- Someone with lots of time on their hands who wants to work on a Haskell project.
- The Haskell Foundation
- Someone else with interest in improving Haskell
Then please reply to this topic.