DEV Community

Cover image for Debunking Haskell Myth: “Tooling Issue”

Posted on • Updated on


Debunking Haskell Myth: “Tooling Issue”

📹 Hate reading articles? Check out the complementary video, which covers the same content:

Haskell is covered with stereotypes and myths. And one of them is the idea that there exists a “Tooling issue” (with a capital T).

Maybe it was true back in the day. But anyways, I want to cover the current state of things and show some tooling that other language ecosystems can only dream about.

And, of course, there are some issues here and there; we’re all software engineers, and we know how it is.

We’re going to cover the following:

  • Installation tools
  • Build tools
  • IDE / Language server
  • Cherry on the top

🌚 Note that we’re not going to talk about Nix.


Today’s standard is to install GHCup, then use it to install the compiler (GHC), the build tools (Cabal, Stack, or both), and the language server (HLS). Afterward, you install VSCode with the Haskell extension (or your favorite editor), and you’re ready.

GHCup – the main Haskell installer.

GHC (Glasgow Haskell Compiler) – the compiler.

Cabal – a Haskell build tool.

Stack – alternative Haskell build tool (based on snapshots).

HLS (Haskell Language Server) – Haskell LSP support.

And this is precisely what we’re going to do.

Note that I’m super lazy; this is almost the setup I’m using. The only configuration I ever do is install another font. But you can take it way further than that.


We can get all we need with one single tool, GHCup. It manages the main Haskell tools: GHC, Cabal, Stack, and HLS. We don’t need to install or manage any of these by hand.

💡 Fun fact: GHCup has nothing to do with cups.

After you install GHCup for the first time, you should have a working Haskell stack on your machine.

You can use the command line interface to upgrade/update the tools; for example, install the recommended version of the language server (hls):

ghcup install hls
Enter fullscreen mode Exit fullscreen mode

But you can also use the tui (text-based user interface):

ghcup tui
Enter fullscreen mode Exit fullscreen mode


You can see from the screenshot which hls versions I have installed and that I also have multiple versions of ghc installed for different projects. And it’s super easy to jump between the versions. I don’t even think about it.

Build tools

There are two main build tools in Haskell: Cabal and Stack. Over the years, their popularity keeps shiftings, and their functionality keeps converging. If you're getting started, you can use either one.

One difference is that Stack uses the curated set of packages by default, while it must be configured in Cabal.

Stackage is a community project that curates these sets and bundles the dependencies known to build together, avoiding any version conflict problems. Which is very appealing. Especially if you have ever experienced ClassNotFoundException at runtime, had to shovel yourself out of the conflicting guava dependencies in Java, or something along these lines.

💡 PureScript has a similar project, called Registry.


Because I’m lazy and want to illustrate it quickly, I’ll show you Stack.

We can initialize and run a project (named, for example, dry-run) with these commands:

stack new dry-run
cd dry-run
stack run
Enter fullscreen mode Exit fullscreen mode

Which will probably print:

Enter fullscreen mode Exit fullscreen mode

🙂 You can imagine that it says: “Hello, World.”

Now, let’s try to change the code.


We only need HLS (which we already have after installing GHCup) and VSCode with the Haskell extension.


And now we have a fully working IDE.

💡 It doesn’t have to be VSCode! You can use your favorite editor with HLS.

HLS provides go-to-definitions, autocompletion, and all the other stuff, which we can use to find the someFunc, change it, and print something else:



someFunc :: IO ()
someFunc = print 42
Enter fullscreen mode Exit fullscreen mode

And then, we can run the project via the terminal.

Now, let’s add a dependency. For example, let’s add aeson – a library for working with JSON. We need to extend the dependencies list in the package.yaml:

- base >= 4.7 && < 5
- aeson
Enter fullscreen mode Exit fullscreen mode

And we don’t need to worry about the library version, the resolver handles the versions for us – it chooses a specific Stackage snapshot. We can check it in the stack.yaml:

Enter fullscreen mode Exit fullscreen mode

And the cool part: if we want to add and use another library, which is also an aeson dependency, for instance, text, we know that the version we utilize in the project is compatible with the version that is required for aeson.

As an illustration, we can use this library to encode a list as JSON. Note that we can use auto-import.

import Data.Aeson (encode)

someFunc :: IO ()
someFunc = print (encode ["Hello", "World"])

-- Prints: "[\"Hello\",\"World\"]"
Enter fullscreen mode Exit fullscreen mode


Have you ever forgotten a name of a function? Or how many minutes have you spent scrolling through the autocomplete suggestion list, searching for a method that fits? Or how often do you have to jump between library docs to find that data structure you need?

What if there was a type-aware search engine that you could ask?

Well, we have this tool in Haskell, and it’s called Hoogle. We can use it to search Haskell libraries by name and type signature.

💡 We also have such tool in PureScript, which is called Pursuit.

Okay, are you ready? Let’s do a couple of searches.

Search for a text

Sometimes, I have to use a function to add a value between the elements of the list. For example, add a comma between the list of words. But I struggle to remember its name – it’s either intersperse, intercalate, or inter- whatever.

I can use Hoogle to search for it:

Enter fullscreen mode Exit fullscreen mode

It shows the type signatures and the docs, so I remember it was intersperse once again.


It’s right there, closer to the bottom:

intersperse :: a -> [a] -> [a]
Enter fullscreen mode Exit fullscreen mode

Okay, nothing crazy; it's as impressive as autocomplete. But!

Search for a type

But we can also search for a type right away.

Let’s start from scratch. What do we need from this function? We need to pass an element and a list (the types of the old and new elements should be the same), and the result should be a new list. So, we can search for it:

element -> [element] -> [element]
Enter fullscreen mode Exit fullscreen mode

It’s right here, on the top:


We can use simpler types to search:

a -> [a] -> [a]
Enter fullscreen mode Exit fullscreen mode

It doesn’t change the result:


Or, imagine we’re looking for a function that checks if the element exists in the list. This time, we change the return type to boolean:

a -> [a] -> Bool
Enter fullscreen mode Exit fullscreen mode

And get something like this:


Which gives us elem and it’s negative twin notElem.

Note that we used a polymorphic type a because we wanted a function that works for any list, but we can search for a more specific type as well:

Int -> [Int] -> Bool
Enter fullscreen mode Exit fullscreen mode

In this case, Hoogle first suggests more specific functions and then generic ones like elem.


And it works with the whole stackage set (or even beyond it) – we can use it whenever we’re stuck looking for a suitable function. If this isn’t cool, I don’t know what is.

💡 Note: You can also install Hoogle locally – run searches on a command line or in a browser, and even hook it up to your editor.


So, yeah, there is no “Tooling Issue” in Haskell. You install one tool and one editor and have all the autocompletes, auto-imports, goto-definitions, etc.

Also, I want to thank everyone who is working on the tooling. You’re the real heroes.

The tools are improving daily, and I’m curious if this walkthrough will age well.

Top comments (1)

nlhnt profile image
Marcin Borawski

Avoid success....