I worked on a compiler in Haskell for a few years, as well as a few non-compiler programming language implementations.
Haskell is great for this in a few ways:
- Garbage collection is really convenient, especially when writing evaluators for lambda calculus like the ones that show up in dependent type checkers
- Haskell makes it reasonable to express datatypes in various different ways - others have referred to Trees that Grow, but I’ve also really enjoyed representing a datatype as an explicit fixed point of a functor - that’s a fancy way to say that the bit that describes which things a type can be (constant, operator, function call, variable reference, etc) is defined separately from the bit that says what the sub-parts are, allowing these to be recombined in interesting ways, e.g. by interposing a source position around each node in one phase, or by replacing nodes with pointers into an explicit-sharing graph structure in some other phase. These techniques are often possible in other languages, but either the type system won’t let you express it clearly or the syntactic and run-time overhead is too great.
- Being explicit about effects in a type signature makes it very clear which parts of the program can do what, which helps keep separate parts separate in a complex program like a compiler.
- Many, many PLs have been implemented in Haskell, so there’s a wealth of libraries, written techniques, and folk wisdom to benefit from.
- The general benefits of Haskell apply here, like having refactorings result in type errors that function as a kind of to-do list for propagating the refactoring throughout the system, instead of needing to use tests for this purpose.
- Many Haskell testing tools are very nice for language development - property-based testing is perhaps most at home in Haskell.
That said, there’s a lot of cool things that can be done in a compiler in Lisp that are hard in Haskell, like having a really low-friction way of writing ASTs as s-expressions that are convenient at a REPL for play and experimentation, having convenient run-time access to the Lisp compiler for making it easier to bootstrap a system, the use of macros to build nice ways to do things like define large sets of primitives, and a language standard that is extremely, extremely stable so code never bitrots. And OCaml is also worth looking into seriously - we don’t have a good competitor to Menhir, for instance.