Setting up Haskell in VS Code on macOS

Matthew Doig
Nov 20, 2016 · 7 min read

Note: If you’re brave and want to try out the remote containers feature for running Haskell in VS Code then see here.

I love VS Code. I love how stripped down it is compared to “enterprise” IDE’s. I love how it runs on your favorite brand of OS, be that Windows, macOS, or Linux. But most of all, I love how easy it is to integrate the language of your choice with the editor.

Search for an extension, click install, and away we go. I’ve been using it for Javascript, Elm, Purescript, ReasonML, F#, and Python development, all without a hitch. The language extensions are universally first rate, with the Ionide plugin for F# development especially impressive.

And once you’ve become familiar using the editor with a language you already know, then it’s trivial to start exploring a language you’ve always wanted to learn. So let’s learn a new language!

All the code for this tutorial can be found here.

Hmm?!? Where to start?

The Haskell Tool Stack

Reading through the instructions in the Haskell VS Code extensions doesn’t really tell you a whole lot about actually setting up a Haskell environment. You’ll see mentions of Cabal and GHCI, perusing the official Haskell documentation doesn’t make it any easier, as it’s still focused on setting everything up with Cabal and sandboxes.

Thankfully, there is a simple way to get started with Haskell, the Haskell Tool Stack, and we’ll set it up with homebrew.

Make sure you’re on the latest version of Xcode and type the following into the terminal.

Now starting a new project is a breeze.

The setup command actually downloads the compiler to an isolated location, so it won’t interfere with any other installations of Haskell on your system. And now we can startup VS Code.

From Editor to IDE

When we first launch VS Code and open a Haskell file we’re greeted by a rather plain looking file.

Yuck! Let’s spruce this poor file up a bit.

The first extension to install is the syntax highlighter. Click install and reload and our sad looking file has gotten a little happier.

Linting Extension

Let’s keep on happyizing our sad little file (I think that’s a word), the next extension we’ll install is the linter based on hlint, which is a package for source code suggestions to make our code simpler and more readable.

After installing you’ll probably see an error like the following.

Unlike, the syntax highlighter we need to do a little bit of configuration grunt work to get the linter extension working.

..Copying from /Users/dogwith1eye/.stack/snapshots/x86_64-osx/lts-7.9/8.0.1/bin/hlint to /Users/dogwith1eye/.local/bin/hlint

Stack will copy the package into our ./local/bin folder, but the hlint path in our Preference file just points to hlint.

What we need to do is add ~/.local/bin to the path in our bash profile.

Open up the .bash_profile in textedit or the editor of your choice and add the following line.

Close down VS Code, crack it back open and Voila! An even happier file!

Language Server

Haskell Language Server adds a number of features we expect from a modern development environment, type information, function definitions, jump to definition, case-splitting, etc.

But, before we can install the plugin, we need to install the haskell-ide-engine for the version of Haskell that Stack has set our project up with. First, let’s check what version of the compiler our project is using.

Stack has configured version 8.6.4 of the compiler for me. So let’s change directories and download the haskell ide engine alongside our project.

Now we need to install the version of the haskell ide engine for compiler version 8.6.4. This seems to take a while, at least on my box, so grab some coffee before running the following.

And finally we can install the actual plugin.

Switching over to our code file we should see the following information about the IO monad when we hover over its definition. Super happy!

Debugging Extension

The last extension we’ll install is to enable debugging support for our Haskell project. So let’s head back into our project and install.

Testing out the Debugger

To test out the debugging extension, we’ll need to set up a testing environment. We’ll begin by running our test suite in stack.

...Test suite not yet implemented

By default our Spec.hs file simply prints to the console that we haven’t set anything up yet. Let’s fix that using the advice from this article. First we’ll alter our package.yaml and add a version of hspec to the dependencies section.

And let’s build our project with the new dependency.

Next, we’ll remove everything from our Spec.hs file and replace it with the following option to automatically discover and run all the spec files in our test suite.

For some reason, I get an error about not being able to execute hspec-discover. I got around this by following the advice on this thread and installing hspec-discover to my global binaries.

And centralize all the dependencies in our test suite with the following SpecHelper.hs file.

Let’s create a function we want to test out.

And finally, we’ll write the actual Spec module to test out our function.

Our project tree should like the following. Remember, unlike a language like F#, in Haskell we have to create folders to correspond to our module declarations.

Running our test now results in the following.

And finally we get to test out actually debugging our spec file. Click on the debug tab of VS Code and choose haskell-debug-adapter, add a breakpoint to our MathSpec file. press F5 to start debugging, and we hit the breakpoint to start debugging our spec. Woot woot!

Remember, all the code for the project can be found here.

Now We’re Ready to Learn Haskell

Much of the difficulty in learning a new language is simply setting up the environment and getting comfortable with the tools to start programming. Thankfully, VS Code let’s us use an editor we’re already comfortable with, so we can concentrate on learning syntax and concepts, instead of tools and workflows.

And so why should we learn Haskell in particular?

Becasue Haskell makes us better programmers. Haskell forces us to solve problems in different ways and forces us to be up front about the side effects we rely upon. At first this may be frustrating, or intimidating, but over time you’ll start finding the ways we used to solve problems (mutations, hidden side effects, iteration) are frustrating and intimidating. Have fun!

Matthew Doig

Written by

A programmer looking for ways to see the forest instead of the trees.