My Haskell VS Code Setup in 2021

Power up your IDE with debugging, autocompletion and other Haskell features

Ivan Hrekov
Better Programming
Published in
9 min readNov 29, 2021

Just a random Haskell meme (Source: https://habr.com/en/post/441350/)

Recently, I started learning Haskell. The first thing you have to do when dabbling in a new language is the development environment configuration. I like VS Code and work primarily in it. Therefore, I had no doubt which editor I’ll use during my learning.

The first thing I did was a quick search for the articles on Medium that would help me to configure the editor. Setting up Haskell in VS Code with Stack and the IDE Engine by Matthew Doig is a good place to start yet it’s a bit outdated. Therefore, I decided to write something similar but with more up-to-date information.

Another point I wanna outline beforehand, I’m using macOS, so keyboard shortcuts and other stuff were tested on the said OS. It may work differently on your laptop or PC.

So, the main problem is that by default VS Code shows no sign of supporting Haskell. Yet the problem could be easily eliminated by following a few simple steps.

The first thing a rookie Haskeller sees is a lack of syntax highlighting

Okay, let’s start from here then.

Step 0. Get Yourself VS Code

Go to VS Code main page and download the installer for your OS of choice.

Yes, it’s as easy as this

From now on, I’ll presume you have VS Code installed, so let’s move on to actually configuring it :)

Step 1. GHCUp It

GHCup is an installer for the general purpose language Haskell.

GHCup is the easiest way to install everything you need to bootstrap your Haskell journey: compiler, packaging and distribution systems, and so on. Sounds great, should we try it out? For sure.

Open the terminal, either inside of the VS Code, or as a stand-alone program, and run the following command inside of it:

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

The program will ask you whether you want to add all the required stuff to your .zshrc or other shell config files. I prefer the appending option, though you’re free to choose for yourself:

After that, GHCup will propose to install Haskell Language Server and stack. These two will definitely come in handy later on, so “Yes” is the answer:

As soon as we’ve chosen all the options we need the installation process should start:

Press Enter to proceed and wait for the whole process to complete. Just a suggestion: grab yourself a cup of tea as this may take a while.

Great, we’re all set. Let’s configure our VS Code now. We need to actually generate a new project for this. I prefer stack for this goal. Therefore, open a terminal window and type the following commands:

$ stack new vscode-haskell-config
$ cd vscode-haskell-config
$ stack setup

Step 2. Syntax Highlighting

The first thing we want to get from a text editor or an IDE is colorized code. It’s easy to achieve this by installing a dedicated extension:

Additionally, it may be needed to reload the editor, so be attentive. After the installation, the app/Main.hs you saw at the beginning should look something like this, depending on your color scheme:

Ah, much better now

That’s definitely an improvement but it’s still quite far from the IDE functionality. In the previous section, we’ve added Haskell Language Server (HLS), and it’d be nice to enable its functionality in VS Code.

Step 3. Pump VS Code Up

The process of configuring the HLS is really complicated except it’s not. There are two simple steps you need to follow to make it work:

̶̶̶1̶̶̶.̶̶̶ ̶̶̶I̶̶̶n̶̶̶s̶̶̶t̶̶̶a̶̶̶l̶̶̶l̶̶̶ ̶̶̶H̶̶̶a̶̶̶s̶̶̶k̶̶̶e̶̶̶l̶̶̶l̶̶̶ ̶̶̶S̶̶̶y̶̶̶n̶̶̶t̶̶̶a̶̶̶x̶̶̶ ̶̶̶H̶̶̶i̶̶̶g̶̶̶h̶̶̶l̶̶̶i̶̶̶g̶̶̶h̶̶̶t̶̶̶i̶̶̶n̶̶̶g̶̶̶ ̶̶̶e̶̶̶x̶̶̶t̶̶̶e̶̶̶n̶̶̶s̶̶̶i̶̶̶o̶̶̶n̶̶̶.̶̶̶ ̶̶̶W̶̶̶a̶̶̶i̶̶̶t̶̶̶ ̶̶̶a̶̶̶ ̶̶̶s̶̶̶e̶̶̶c̶̶̶…̶̶̶ ̶̶̶W̶̶̶e̶̶̶ ̶̶̶f̶̶̶i̶̶̶n̶̶̶i̶̶̶s̶̶̶h̶̶̶e̶̶̶d̶̶̶ ̶̶̶i̶̶̶t̶̶̶ ̶̶̶j̶̶̶u̶̶̶s̶̶̶t̶̶̶ ̶̶̶a̶̶̶ ̶̶̶f̶̶̶e̶̶̶w̶̶̶ ̶̶̶m̶̶̶i̶̶̶n̶̶̶u̶̶̶t̶̶̶e̶̶̶s̶̶̶ ̶̶̶a̶̶̶g̶̶̶o̶̶̶…̶̶̶ ̶̶̶C̶̶̶o̶̶̶o̶̶̶l̶̶̶,̶̶̶ ̶̶̶h̶̶̶u̶̶̶h̶̶̶?̶̶̶ ̶̶̶T̶̶̶h̶̶̶u̶̶̶s̶̶̶,̶̶̶ ̶̶̶t̶̶̶h̶̶̶e̶̶̶ ̶̶̶s̶̶̶i̶̶̶n̶̶̶g̶̶̶l̶̶̶e̶̶̶ ̶̶̶s̶̶̶t̶̶̶e̶̶̶p̶̶̶ ̶̶̶l̶̶̶e̶̶̶f̶̶̶t̶̶̶.̶̶̶

1. Install HLS support extension:

Click the “Install” button and wait for the editor to do the remaining magic. Here you go. After installation, our turn-to-be IDE shows a red squiggle in our app/Main.hs:

It’s alive!

To resolve the issue we need to be inside the project’s directory in our terminal, and type these commands:

$ stack clean --full
$ stack build

Everything should be in color now:

HLS is an extremely useful tool. It provides our VS Code (and us, ofc) with:

  • type information and documentation on hover
Awesome, right?
  • autocompletion
Not so ideal though
  • refactoring and code style suggestions from hlint
A skilled peer reviewer for free :)
  • other handy features that are expected to get from an IDE

Any Code Formatting?

That’s a nice question. Actually, HLS provides you with those capabilities though I haven’t told you that on purpose. There are several formatting providers with a default called Ormolu. I strongly recommend you try them all and choose the one that will satisfy your needs and taste.

Nevertheless, my personal preference is Brittany. This is my article, therefore, you’re out of luck if expected to see something different here :)

Thus, I’ll show how you can easily change the formatting provider to Brittany.

In order to do so, press this combination on your keyboard Cmd + , or click Code -> Preferences -> Settings:

Just an illustration for your convenience

Ready? Great, moving further. Now type in “Haskell” in the settings’ search bar, and find the “Formatting Provider” section where you should see Ormolu selected:

Just an illustration for your convenience (2)

Now, let’s actually select a different one, which is Brittany:

One Brittany to format them all, so to speak

Okay, what if we want to ensure it actually works, because we don’t trust those fancy editors? First thing first, we need to add some code to see the formatting in action.

Let’s open the ./src/Lib.hs file as we’ll be placing our brand new function there. You may erase the whole content of the file, no worries.

Please, retype the example with these inconsistencies as we’ll format it automatically.

“Before”

Wait a sec… How do we use the formatter anyway? Press Opt + Shift + F, and Brittany will do the heavy lifting.

Please, don’t worry if you don’t understand what’s going on, the point was to test the automatic formatting. It seems to work just fine :)

“After”

Haskell Code Debugging

Despite the notion that if Haskell code compiles it works as intended, the debugging capabilities may be useful. Unfortunately, HLS does not seem to bring a debugger (at least as of November 2021.)

Fortunately, this “weakness” is easily resolved by the installation of another cool extension.

Did you just install it? That awesome! We’re almost set to start debugging. Before the actual start, we have to a) add tests and b) configure the debugger.

a) Adding Tests

If we try to run the debugger without any tests, it’ll exit saying “Test suite not yet implemented”.

That’s exactly the thing we’re getting from running

$ stack test

We can observe this string in test/Spec.hs:

Aaaah, here you are

Okay, fine. It seems we just need to implement some tests. Sounds reasonable.

We’ll be using QuckCheck for that. It’s an awesome library allowing us to do some property-based testing. We’ll write only one test for the pal function as we need to test the debugger, and not write a proper test suite.

First of all, let’s add QuickCheck to our project. Go to package.yaml, and in the tests section find the dependencies subsection. Feel free to add the library as a dependency:

See line 49, Added? package.yaml looks bigger now

The dull part is almost over, we’re about to dive into the awesome world of property-based testing. Please, open the ./test/Spec.hs file again. The important thing to decide in the beginning: what would be tested? Since we don’t have tons of options, just import the pal :)

import Lib (pal)

The next step is adding another import, this time QuickCheck. After this, your Spec.hs has the following contents:

import Test.QuickCheck
import Lib (pal)

Let’s write our first property test. We’re gonna name it prop_reverseInvariant which checks that the pal function applied to the reversed palindrome should also return True.

import Test.QuickCheck
import Lib (pal)
prop_reverseInvariant :: String -> Bool
prop_reverseInvariant text = pal text == pal (reverse text)

After some formatting with Brittany, the content of the file would be as follows:

Not baaaaad… Not bad at all

Note. You may have encountered the following issue:

Warning: Installation path /Users/<username>/.local/bin not found on the PATH environment variable.

All you need to do in order to fix the issue is to add this to your .zshrc or other shell configuration file:

export PATH=$PATH:~/.local/bin

b) Configuring the Debugger

Choose “Run and Debug” sidebar button in VS Code:

The one with a tiny bug on the play button

What’s next? We need to create a launch.json file first, in order to configure all the stuff related to “how should the IDE run our debugger?” Do this by clicking “create a launch.json` file. Select haskell-debug-adapter in the dropdown:

Congrats, the .vscode directory with respective configuration was created in the root of the project. But… We’re interested in actually running the debugger and not doing all that boring stuff. Please, be patient :)

Add a few lines to the test/Spec.hs file:

main :: IO ()
main = do
quickCheck prop_reverseInvariant
Spec.hs now looks close enough to this

Insert a breakpoint on line 9, and run the debugger with the haskell(stack) option. Make sure it actually stops the execution, ’cause that’s the point :)

Have a Nice Haskell Journey with Configured IDE

That’s mostly it when we’re talking about setting the VS Code for Haskell. Now you have all the syntax highlighting and IDE functionality, as well as instruments to debug our code.

There might be other things useful in development such as ghcid. Though it has no connection to VS Code, thus, feel free to explore those tools by yourself :)

What are you waiting for? Enjoy learning Haskell and have fun! :)

UPD. I received some feedback on LinkedIn and Reddit, which I consider rather useful but don’t wanna take credits for.

You may find some interesting comments on r/haskell thread. Check it out.

Another point I wanted to add was outlined by Ramón Soto Mathiesen (many thanks for that) on LinkedIn, regarding -with-rtsopts=-N -qg GHC flag. This paste may make it clearer why you may need to add that.

No responses yet

What are your thoughts?