Configuring an editor for a new language is a double-edged sword: it’s intensely satisfying when done, but takes time away from diving into the language itself! After using Haskell for a little over a year, I’ve settled on a high-quality set of editor plugins. They’re simple, powerful, and all play nicely together.
I use Haskell Stack exclusively. Stack’s goal is reproducible builds, which means that in general, things Just Work.
I also use Neovim, rather than normal Vim. Usually, my justification is ideological rather than technical. However, for Haskell my setup requires Neovim. Fear not! Neovim is feature-packed and also very stable. I love Neovim, and I’ll be writing more about why in a future post.
By the way, new to Vim plugins? I happen to have just the post for you!
We’re going to move in order of increasing complexity. That said, even the most “complex” plugin here is actually quite painless to set up. By the end, we’ll have a complete development experience! Coming up:
- syntax highlighting & indentation (haskell-vim)
- auto-formatting & style (hindent, stylish-haskell)
- quickfix and sign column support (using ale) for:
- linter style suggestions (hlint)
- compiler errors and warnings (ghc-mod)
- Type inspection, REPL integration, and more! (intero-neovim)
To keep things concise, I’ve moved all the relevant configuration to the end of the post. For now, let’s start at the top.
Syntax Highlighting & Indentation
Vim’s default Haskell filetype plugin is pretty lack luster. Everything is blue, except for strings which are colored like comments, and keywords which are colored like constants. Indentation is wonky in some cases, and isn’t configurable.
This plugin corrects all that. It’s the filetype plugin for Haskell that should ship with Vim.
Not only does it come with saner defaults, it also comes with more config options, especially for indentation. This is important because it lets me tweak the automatic indentation to my own personal style.
(Remember: all the config is at the end of the post.)
Auto-formatting and Indentation
- Plugin: vim-hindent
stack install hindent
stack install stylish-haskell
For small projects, I have an idea of what style I like best. However, for larger projects it’s unfair to ask contributors that they learn the ins and outs of my style. Situations like these call for automated solutions.
go fmt famously solved this problem for Golang by building the formatting tool
into the compiler. For Haskell, there’s hindent2.
hindent can be
installed through Stack, and
vim-hindent is a Vim plugin that shims it.
But I said I’m partial to my own style in personal projects. There’s another
Haskell formatter that’s much less invasive: stylish-haskell. It basically
case branches, and record fields, aligning them
vertically. And in fact, it’s possible to use this alongside
With these three tools, I can pick the right tool for the job:
- Hand saw: let
haskell-vimconfig control the indentation
- Table saw: run
- Chainsaw: run
- Chainsaw, then sand paper: run
Getting them to play together requires a bit of config, so I’ve included mine at the end of the post.
Quickfix & Sign Columns
- Plugin: ale
stack install hlint
stack build ghc-mod
- N.B.: This is build not install here3.
This step requires either Neovim or Vim 8; ALE stands for “Asynchronous Lint Engine,” so it’s using the new asynchronous job control features of these two editors. It’s like an asynchronous Syntastic4.
ALE ships with a number of Haskell integrations by default. For example, it can
show errors if only Stack is installed. I prefer enabling two of ALE’s Haskell
hlintis a linter for Haskell. It warns me when I try to do silly things like
if x then True else False.
ghc-modis a tool that can check files for compiler errors.
The beauty of ALE is that it works almost entirely out of the box. The only real setup is to tell ALE to use only these two integrations explicitly. I’ve included the one-liner to do this in the config at the bottom.
Intero: The Pièce de Résistance
- Plugin: intero-neovim
Intero is a complete development program for Haskell. It started as an Emacs package, but has been ported almost entirely to Neovim. Probably the best way to introduce it is with this asciicast:
Intero is designed for stack, sets itself up automatically, has point-and-click type information, and lets me jump to identifier definitions. On top of it all, it uses Neovim to communicate back and forth with a terminal buffer so that I get a GHCi buffer right inside Neovim. For Emacs users, this is nothing new I’m sure. But having the REPL in my editor continues to blow my mind 😮.
Developing with the REPL in mind helps me write better code. Only top-level bindings are exposed in the REPL, so I write more small, testable functions. See here for more reasons why the REPL is awesome.
On top of providing access to the REPL, Intero provides about a dozen convenience commands that shell out to the REPL backend asynchronously. Being able to reload my code in the REPL—from Vim, with a single keystroke!—is a huge boon when developing.
Intero takes a little getting used to, so be sure to read the docs for some sample workflows. Intero also sets up no mappings by default, so I’ve included my settings below.
The Eagerly-Awaited Config
And without further ado…
With these tools, I feel empowered (rather than hindered) when I sit down to work with Haskell.
- The entire setup uses Stack, so things Just Work.
- As a consequence, everything works with the implicit global Stack project!
- It scales up in power:
- From simple syntax highlighting and manual indentation…
- to an indentation chainsaw and a REPL embeded in the editor!
- I can take full advantage of all my tools working together, leading to cleaner code and fewer frustrations.
Now that I’m finally at a point where I can stop fretting about my Haskell setup, I’ll have more time to explore the language and write about my experience.
Haskell-the-language isn’t quite on the same level as SML-the-language, but it’s far and above when comparing by tooling support. I’m looking forward to taking advantage of that!
While listed under “neovimhaskell” on GitHub, this plugin works with normal Vim, too.↩
Chris Done explains the appeal of solving style issues with tooling for Haskell well. The moral of the story is that hindent version 5 ships with only the most popular style formatter in an effort to arrive at a singular Haskell style: http://chrisdone.com/posts/hindent-5↩
We want to install
ghc-modonce in every project. It can be done globally, but it might get out of sync with the current project.↩
Some people are familiar with Neomake for this task. However, Neomake is much more minimal than ALE. Neomake basically only builds, whereas ALE is more configurable and hackable.↩