Since I started developing in Haskell, I have experimented with a few different setups. I started with Vim with no plugins and that, together with GHC in the terminal was my vanilla setup as I spent my first couple of months learning the basics of Haskell. I like to start off this way when learning a new language as it really forces me to commit the basics of the language and its syntax to memory.
As I learned more about the language and moved on to more advanced topics and more complex problems, I needed to take advantage of Haskell tooling to increase my productivity. Sometimes I find the tooling for Haskell can get a bad rep but I actually find it very good. Tools like ghc-mod, hlint and stylish-haskell provide a lot of key features that you may be used to from working with IDEs – code completion, documentation, compile error and warnings and even suggestions to make your Haskell code more idiomatic. The tricky thing can be getting these tools integrated into your favourite editor or IDE.
I started with VIM plugins to integrate these tools with VIM and also played around with the Haskforce plugin for Intellij which is an excellent plugin. I finally settled on the Atom editor which makes integrating Haskell tools very simple and it also has a load of really great plugins to enhance your Haskell development experience.
In this post, I’m going to go through step by step how to set up a Haskell development environment using Stack (a Haskell build tool), ghc, ghc-mod and other wonderful Haskell tools, and integrating all this with the Atom editor.
To make this a bit more realistic and to make sure that I don’t miss anything, I’m stripping my laptop of any Haskell, Stack, ghc and other installations related to Haskell tooling. I will leave Atom installed but I’ll remove all my Haskell plugins and start from scratch. As I set up my environment from scratch, I will document everything with command line commands and screenshots. I am using a Mac with macOS Sierra.
Okay here goes…
Firstly, I use Homebrew for installing Stack.
Setting up Stack, Ghc and other Haskell tools
➜ ~ brew install haskell-stack
➜ ~ stack setup
This may take about 10 minutes and you will see something like the following output.
Using latest snapshot resolver: lts-7.13 Writing implicit global project config file to: /Users/tom/.stack/global-project/stack.yaml Note: You can change the snapshot via the resolver field there. Downloaded lts-7.13 build plan. Fetched package index. Populated index cache. Preparing to install GHC to an isolated location. This will not interfere with any system-level installation. Downloaded ghc-8.0.1. Installed GHC. stack will use a sandboxed GHC it installed For more information on paths, see 'stack path' and 'stack exec env' To use this GHC and packages outside of a project, consider using: stack ghc, stack ghci, stack runghc, or stack exec
We can now start up the Haskell compiler, ghc, in interactive mode, ghci
In other words lets crank up a Haskel REPL and try a Haskell expression:
➜ ~ stack ghci Configuring GHCi with the following packages: GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help Loaded GHCi configuration from /Users/tom/.ghci Loaded GHCi configuration from /private/var/folders/tj/mtvt3dv57cq6kbzw8809_gs00000gn/T/ghci78459/ghci-script ghci> 1 + 1 2
We can quit back out of ghci now
We can now use stack to set up the tools that will be the engine behind a lot of our Haskell development experience.
➜ ~ stack install ghc-mod hlint stylish-haskell
You will see something like
[1 of 2] Compiling Main ( /Users/tom/.stack/setup-exe-src/setup-mPHDZzAJ.hs, /Users/tom/.stack/setup-exe-src/setup-mPHDZzAJ.o ) [2 of 2] Compiling StackSetupShim ( /Users/tom/.stack/setup-exe-src/setup-shim-mPHDZzAJ.hs, /Users/tom/.stack/setup-exe-src/setup-shim-mPHDZzAJ.o ) Linking /Users/tom/.stack/setup-exe-cache/x86_64-osx/tmp-Cabal-simple_mPHDZzAJ_188.8.131.52_ghc-8.0.1 ... ghc-paths-0.1.0.9: download
It will continue on and download all the packages it needs. This can take a while.
comonad-5: download comonad-5: configure polyparse-1.12: copy/register cpphs-1.20.2: download comonad-5: build ... Progress: 54/71 ...
You should eventually see something like
Copying from /Users/tom/.stack/snapshots/x86_64-osx/lts-7.13/8.0.1/bin/hlint to /Users/tom/.local/bin/hlint Copying from /Users/tom/.stack/snapshots/x86_64-osx/lts-7.13/8.0.1/bin/stylish-haskell to /Users/tom/.local/bin/stylish-haskell Copied executables to /Users/tom/.local/bin: - ghc-mod - ghc-modi - hlint - stylish-haskell
Stack has now added executables in /Users/tom/.local/bin (/Users/tom being my home directory or ~)
You can add this to your PATH environment variable. I do this by editing ~/.zshrc as I use Zsh for my shell. If you are using bash, you can edit .bashrc
In the line for export PATH=
add this to the start of your path
except the /Users/tom part needs to be replaced with the path to your home directory.
Setting up Atom
Download and install the Atom editor. It can be downloaded from here
So lets crank up this beautiful and highly configurable editor!
To start adding our Haskell Atom packages, click “Install a Package” and then “Open Installer”
Search for haskell
Click “Install” on the following
These should be all you need to get going but there are also other great packages that are worth exploring.
For example, haskell-hoogle lets you get documentation from Haskell Hoogle through a context menu.
There shouldn’t be any other configuration needed.
Lets set up a Haskell Stack Hello World project and edit it in Atom.
Close Atom for now.
back in the terminal, we can create a Stack project called hello.
➜ ~ stack new hello
This will show something like this
Downloading template "new-template" to create project "hello" in hello/ ... The following parameters were needed by the template but not provided: author-email, author-name, category, copyright, github-username You can provide them in /Users/tom/.stack/config.yaml, like this: templates: params: author-email: value author-name: value category: value copyright: value github-username: value Or you can pass each one as parameters like this: stack new hello new-template -p "author-email:value" -p "author-name:value" -p "category:value" -p "copyright:value" -p "github-username:value" Looking for .cabal or package.yaml files to use to init the project. Using cabal packages: - hello/hello.cabal Selecting the best among 9 snapshots... * Matches lts-7.13 Selected resolver: lts-7.13 Initialising configuration using resolver: lts-7.13 Total number of user packages considered: 1 Writing configuration to file: hello/stack.yaml All done.
You’ll see there that it prints out info about .cabal. Cabal is a Haskell build tool that Stack is built on top of.
So lets open this up in Atom and see what it looks like. My main pain point with Haskell and Atom was getting Atom to play nicely with ghc-mod through Stack. The following way of opening a Stack project in Atom is officially deprecated but I still find it the simplest way to open a Stack project in Atom and have it work with no hassle.
Go into the hello directory.
➜ ~ ls -l hello
Open Atom through Stack
➜ hello stack exec atom
Click on “Open a Project” and the the “Open a Project” button
Select your hello directory and click open.
To open the main.hs file that stack has generated for us:
and type in “main”
You will see a haskell-ghc-mod warning appear.
This seems to be a side effect of using stack exec to open the Stack project in atom. This warning popup can be closed and ignored. I have found no bad effects of ignoring this warning anyway.
We can now see the main.hs open with our nice Haskell syntax highlighting.
I have gone ahead and typed “i” under the module declaration for and you can see the code complete for importing a module.
If I type “im” and tab, it inserts a template for module import. There a lot of these handy templates including data declarations do syntax, if then else etc
If I start typing out an import for Data.List, you can see auto complete of ghc-mod giving me my options.
I can also go ahead and start typing “put” into main and I get auto complete options again.
If I make a mistake and save, ghc-mod gives me my compile error in the bottom pane and also when I hover over the place where the error occurs.
So lets fix that and print “Hello World”
Now we can go back to our terminal and build this.
➜ hello stack build Warning: File listed in hello.cabal file does not exist: README.md hello-0.1.0.0: build (lib + exe) Preprocessing library hello-0.1.0.0... [1 of 1] Compiling Lib ( src/Lib.hs, .stack-work/dist/x86_64-osx/Cabal-184.108.40.206/build/Lib.o ) Preprocessing executable 'hello-exe' for hello-0.1.0.0... [1 of 1] Compiling Main ( app/Main.hs, .stack-work/dist/x86_64-osx/Cabal-220.127.116.11/build/hello-exe/hello-exe-tmp/Main.o ) Linking .stack-work/dist/x86_64-osx/Cabal-18.104.22.168/build/hello-exe/hello-exe ... Warning: File listed in hello.cabal file does not exist: README.md hello-0.1.0.0: copy/register Installing library in /Users/tom/hello/.stack-work/install/x86_64-osx/lts-7.13/8.0.1/lib/x86_64-osx-ghc-8.0.1/hello-0.1.0.0-2MDzoFPW8yDJLgrpJYcMkC Installing executable(s) in /Users/tom/hello/.stack-work/install/x86_64-osx/lts-7.13/8.0.1/bin Registering hello-0.1.0.0...
Stack creates an executable for us called hello-exe. This name is configured in hello.cabal in our hello directory.
We can run this executable now.
➜ hello stack exec hello-exe hello world
Finally, that ghc-mod warning that was coming up in Atom:
cmd-, will bring up the settings pane.
Go into packages, haskell-ghc-mod and Settings and scroll down and check “Suppress GHC Package Path Warning”. However, due to the warning there, it might be better not to do this and to just close out of the ghc-mod warning popup with you open up a project.
Hopefully this guide has been useful. Enjoy playing with Atom and its packages for Haskell!
For existing projects, if they were built with stack that had a different lts-resolver, you can get problems with atom.Easiest thing there is to delete the .stack-work directory in the root directory of your project. In terminal run this command from the project root directory:
stack init --solver --force
this creates a new stack.yaml file with the lts-resolver for your installed stack. Removing .stack-work may cause a lengthy rebuild if the existing project has a lot of dependencies.
My projects have small numbers of dependencies at the moment so it hasn’t been a problem for me.
Then rebuild your project with
and open atom with
stack exec atom
from your project’s root directory as before.