At Digital Asset, we wrote the DAML programming language. The compiler builds on GHC, and one of the important tools for using DAML is an IDE. You can try the DAML IDE online or download it. Since we wrote the DAML IDE in Haskell, and DAML uses GHC under the hood, it’s possible to take the work we did for the DAML IDE and turn them into pieces for a Haskell IDE. In the rest of this post I’ll outline what we wrote, and how I think it can make a full Haskell IDE.
What has Digital Asset written?
We have written a Haskell library
hie-core, which serves as the “core” of an IDE. It maintains state about which files are open. It generates diagnostics. It runs the parser and type checker. It doesn’t figure out how to load your package, and it doesn’t have integrations with things like HLint etc. In my view, it should never gain such features – it’s deliberately a small core of an IDE, which can be extended with additional rules and handlers after-the-fact.
On the technical side, at the heart of the IDE is a key-value store, where keys are pairs of file names and stages (e.g.
TypeCheck) and values are dependent on the stage. We use the Shake build system in memory-only mode to record dependencies between phases. As an example of a rule:
define $ \TypeCheck file -> do pm <- use_ GetParsedModule file deps <- use_ GetDependencies file tms <- uses_ TypeCheck (transitiveModuleDeps deps) packageState <- use_ GhcSession "" opt <- getIdeOptions liftIO $ Compile.typecheckModule opt packageState tms pm
To type check a file, we get the parse tree, the transitive dependencies, a GHC session, and then call a
typecheckModule helper function. If any of these dependencies change (e.g. the source file changes) the relevant pieces will be rerun.
Building on top of Shake wasn’t our first choice – we initially explored two painful dead ends. While Shake isn’t perfect for what we want, it’s about 90% of the way there, and having robust parallelism and many years of solid engineering is worth some minor compromises in a few places. Having all the features of Shake available has also been exceptionally helpful, allowing us to try out new things quickly.
What else is required for an IDE?
My hope is that
hie-core can become the core of a future IDE – but what else is required?
- Something to load up a GHC session with the right packages and dependencies in scope. For DAML, we have a custom controlled environment so it’s very easy, but real Haskell needs a better solution. My hope is that
hie-biosbecomes the solution, since I think it has a great underlying design.
- Some plugins to add features, such as the as-yet-unwritten
hie-ormolu. Since we add lots of features on top of
hie-coreto make the DAML IDE, we have a good story for extensions in
hie-core. Importantly, because
shakeis designed to be extensible, these extensions can integrate with the full dependency graph.
- Something to talk Language Server Protocol (LSP) to communicate with editors, for which we use the existing
- An extension for your editor. We provide a VS Code extension as
hie-core, but it’s a fairly boilerplate LSP implementation, and people have got it working for Emacs already.
- Something to put it all together into a coherent project, generate it, distribute it etc. A project such as
haskell-ide-enginemight be the perfect place to bring everything together.
Can I try it now?
- The IDE doesn’t load all the relevant files, only the ones you have open.
- Integration with things like
stackdoesn’t work very well – I’ve been using
hie-biosin “Direct” mode – giving it the flags to start
ghcimyself. See my integrations for
- Features like
hs-bootfiles and Template Haskell need more work to be fully supported, although a bit of Template Haskell has been observed to work.
These issues are being discussed on the
hie-bios issue tracker.
Q: Is something like FRP better than Shake for describing dependencies? A: I think it’s clear that an IDE should use some dependency/incremental computation/parallel rebuilding approach. Shake offers one of those, and is well tested, exception safe, performant etc. The mapping from Shake to what we really want is confined to a single module, so feel free to experiment with alternatives.
Q: What is the relationship to haskell-ide-engine? My hope is this piece can slot into the other great things that have been done to make IDE tooling better, specifically
haskell-ide-engine. This post is intended to start that discussion.