pema.dev

Setting up Haskell Language Server with VSCode is cursed

Haskell setup is pain

I’ve been enjoying writing Haskell a lot lately, but my god has the tooling been a mixed experience. I recently bought a new laptop, which I’ve installed Arch Linux on, and wanted to set up the Haskell language server (HLS) to work with Visual Studio Code on it. Every time I’ve attempted this in the past, it has been extraordinarily painful. Here is a cheatsheet to my future self as well as anyone who may find it useful.

Template Haskell

The project I am working on uses template Haskell fairly heavily. Unfortunately, most easy ways to install HLS default to static linking, which doesn’t play well with with template Haskell. In fact, it plays so badly that the language server will routinely segfault on my project on several machines I’ve tried to do this on.

GHCUP to the rescue

By far the least painful way I have found to get this all working is using ghcup. I recommend installing every piece of tooling needed with it (ghc, cabal, hls, stack).

Installing GHCUP

If you have Haskell installed in any other way, uninstall it, and follow the steps on https://www.haskell.org/ghcup/. Concretely, run:

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

You will prompted to install a few different tools. Only install ghc, cabal, and stack, but decline to install the language server. Make to sure to allow the tool to add the tools to your $PATH.

If your project needs as specific version of ghc, you can easily install and switch to it using the ghcup tui interface. My project used GHC 9.0.2.

Installing HLS

For the language server, we need to build it from source to support template haskell properly. There are some steps here https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html#static-binaries. Since we are using GHCUP, run something like:

ghcup compile hls -v 1.6.1.0 --ghc 8.10.7

With your appropriate version. Some versions apparently need a specific cabal.project, so I ran:

ghcup compile hls -v 1.6.1.0 --ghc 9.0.2 --cabal-project cabal-ghc90.project

That will compile HLS, which might take a little while.

Making VSCode aware of binaries from GHCUP

GHCUP will place a line into your .bashrc that looks like so:

[ -f "/home/pema99/.ghcup/env" ] && source "/home/pema99/.ghcup/env" # ghcup-env

Which works fine when using the installed binaries via a shell. Unfortunately, under normal circumstances, the $PATH VSCode uses will not be affected by this. (More info here https://github.com/haskell/haskell-language-server/issues/236). We have a few options to fix that:

  • Add this same line from above, to the bottom of your /etc/profile file. This is a bit of a hack, but it is what I did.
  • Start VSCode from the terminal with the flag --force-user-env. Super tedious.
  • Use the haskell.serverEnvironment setting in your VSCode settings.json to manually make the Haskell extension aware of where the binaries installed by GHCUP are (info here https://github.com/haskell/haskell-language-server/issues/236#issuecomment-987174473)

Setting up the Haskell extension

For whatever reason, the VSCode Haskell extension doesn’t seem to play nicely with projects that have never been built, so I suggest doing the following:

  • If you don’t already have the Haskell extension installed, open a fresh VSCode instance and install it from the marketplace. https://marketplace.visualstudio.com/items?itemName=haskell.haskell
  • Optionally make the edit to your settings.json if you opted for that in the previous step.
  • Close VSCode
  • Navigate to your Haskell project in a shell
  • Assuming you are using stack to build, do stack setup then stack build.
  • Now, open VSCode in your project directory.

The end

If you followed every step correctly, VSCode should now automatically figure out your setup, start compiling your code, and hook up the language server. Or maybe not, if I made some stupid blunder while writing this. Let’s hope not, future me will surely be annoyed.