Monthly Archives: December 2016

Setting Up A Haskell Development Environment (Mac OS)

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

ghci>  :q

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_1.24.0.0_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
“/Users/tom/.local/bin:
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
language-haskell
haskell-ghc-mod
ide-haskell
autocomplete-haskell

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:
cmd-p
and type in “main”

Select main.hs
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-1.24.0.0/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-1.24.0.0/build/hello-exe/hello-exe-tmp/Main.o )
Linking .stack-work/dist/x86_64-osx/Cabal-1.24.0.0/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!

Additional Note

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

stack build

and open atom with

stack exec atom

from your project’s root directory as before.