Debunking Haskell Myth: “Tooling Issue”
Tags: haskell, functional, tooling, beginners
Published: 2023-02-28 17:36:36

📹 Hate reading articles? Check out the complementary video, which covers the same content: https://youtu.be/c7FncTzvpUQ

---

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.

---

## Speed-run

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.*

## Installation

We can get all we need with one single tool, [GHCup](https://www.haskell.org/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
```

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

```
ghcup tui
```


![ghcup-tui](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2itg1nyven68zmkn73vc.png)

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](https://cabal.readthedocs.io/en/stable/) and [Stack](https://docs.haskellstack.org/en/stable/). 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](https://www.stackage.org/) 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](https://github.com/purescript/registry).

---


![stack-overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ltp0u6g5uqsjh23ofwr5.png)

*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
```

Which will probably print:

```
someFunc
```

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

*Now, let’s try to change the code.*

## IDE

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


![hls-extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1u7c6v5tuze5djrrgsrv.png)

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:


![hls-goto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pd9w7bkf0kmbk2oe93ic.png)

![hls-print](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzf7jz0ixb76ss0orzho.png)

```haskell
someFunc :: IO ()
someFunc = print 42
```

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`:

```yaml
dependencies:
- base >= 4.7 && < 5
- aeson
```

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`:

```yaml
resolver:
  url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/20/11.yaml
```

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.

```haskell
import Data.Aeson (encode)

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

-- Prints: "[\"Hello\",\"World\"]"
```

## Hoogle

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`](https://hoogle.haskell.org/). We can use it to search Haskell libraries by name and type signature.

---

💡 We also have such tool in PureScript, which is called [`Pursuit`](https://pursuit.purescript.org/).

---

*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](https://hoogle.haskell.org/?hoogle=inter):

```
inter
```

It shows the type signatures and the docs, so I remember it was [`intersperse`](https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-List.html#v:intersperse) once again.

![hoogle-text-inter](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5oep7sv9zqeuxj5ihpd7.png)

It’s right there, closer to the bottom:

```
intersperse :: a -> [a] -> [a]
```

*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](https://hoogle.haskell.org/?hoogle=element%20-%3E%20%5Belement%5D%20-%3E%20%5Belement%5D):

```
element -> [element] -> [element]
```

It’s right here, on the top:

![hoogle-type-intersperse](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52xh82bppby435p0gx88.png)

We can use simpler types to [search](https://hoogle.haskell.org/?hoogle=a%20-%3E%20%5Ba%5D%20-%3E%20%5Ba%5D):

```
a -> [a] -> [a]
```

It doesn’t change the result:

![hoogle-type-intersperse-a](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ju5nx13dschxbo2gg73k.png)

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](https://hoogle.haskell.org/?hoogle=a%20-%3E%20%5Ba%5D%20-%3E%20Bool):

```
a -> [a] -> Bool
```

And get something like this:

![hoogle-type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ie6kzsp37rqc0fvy89qx.png)

Which gives us [`elem`](https://hackage.haskell.org/package/base-4.17.0.0/docs/GHC-List.html#v:elem) and it’s negative twin [`notElem`](https://hackage.haskell.org/package/base-4.17.0.0/docs/GHC-List.html#v: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
```

In this case, Hoogle first suggests more specific functions and then generic ones like [`elem`](https://hackage.haskell.org/package/base-4.17.0.0/docs/GHC-List.html#v:elem).

![hoogle-type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xkoaryl7r59md7z0k82.png)

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.

---

## Conclusion

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.*