Easy Haskell Development Setup with Docker

Haskell, a statically typed, purely-functional programming language, is not
only fun to develop with, but can make you a better programmer just by learning
it. If you’ve never tried it, I highly suggest you try your next side project
in Haskell. We can even help you get started.

If you have used it before, you’ll know that getting started can be a long
process. Compiling GHC, cabal, or a long list of dependencies can take
minutes to hours and all you really want to do is code! You might also run into
dependency issues if you’re not sandboxing correctly which can cause errors
that will frustrate and confuse even the most patient programmers. I want your
first and every experience with Haskell to be enjoyable! Using Docker, we
abstract the setup and allow you to focus on the fun parts of Haskell: Haskell
itself.

In order to make Haskell development smoother, we’ve created a few Docker
images that you can use. They start containers that come installed with the
base dependencies needed to develop with Haskell and with Yesod, a popular
web development framework. We’re going to walk you through the process of
installing and setting up Docker for you machine. Then we’ll get you right into
a working Haskell development environment. Finally, we’ll look at creating a
Yesod project and setup your development environment.

We first need to install Docker. The installation instructions are a little
different depending on your system. We’ve outlined a couple below but if you
are looking for more detail or for the instructions of your specific system you
can follow Docker’s install instructions.

If you’re on a Mac, you’ll also need to install boot2docker which is a set of
tools wrapped around a minimal Linux Virtual Machine for Docker to live inside.
boot2docker requires VirtualBox installed first. So the complete install
instructions for using Homebrew on a Mac are:

Great! Now start boot2docker by running boot2docker init then boot2docker
up. It should print three export commands. Copy those and run them in your
terminal. Now you’re ready to use docker! If you switch terminal contexts and
need those export statements again you can run boot2docker shellinit and
copy-paste them.

You can add boot2docker to your launch control so you don’t have to remember
to start it after every restart. In your terminal run the following:

If you just want to mess around with Haskell, you can easily boot into our
latest GHC image and run ghci to open a REPL.

docker run --rm --interactive --tty thoughtbot/ghc

--rm disposes of the container when we’re done while --interactive and
--tty allow us to interact with the process running inside the container.

You could also make your own Dockerfile that inherits from this one to give
you the base GHC install and dependencies. For example, if you wanted to have
an image that starts with Yesod installed you could create this Dockerfile:

FROM thoughtbot/ghc
RUN cabal update
RUN cabal install happy yesod-bin

And we’ve done just that to provide you with a default Yesod setup for your
web projects.

OK, your project is setup, what next? To run the base project you’ll have to
compile your dependencies and link a database. We have your back here, too.
We’ve created a base Yesod image to get you started. You’ll have to make a
Dockerfile and a docker-compose.yml file for your project, but no fear,
we’re going to walk you through it.

The keyword FROM specifies our base image, then we create our app directory
and set it as our working directory. Next, we copy the .cabal file from the
newly created Yesod project into the container. Finally, we install the
dependencies making sure to enable tests.

There is a bit more going on in this file, but it’s not bad, I promise! First,
we create our database container calling it db. It uses the base Postgres
version 9.4 image hosted on Docker Hub and opens up the port 5432 which is
the default port used by Postgres. Then we setup our Yesod app naming it web.
We set it to build the image from the Dockerfile in the current directory and
then run the command yesod devel. Next, we setup our environment with the
database configuration, keep stdin open (required for the yesod devel
command), mount our current directory to the container’s /app folder, map the
port 3000, and finally link the db container to the web container.

Almost there! All that’s left now is to create your database. If you look at
the config/settings.yml file that yesod init created, you’ll see the name
of the database that Yesod is expecting to find. We can create this database in
our db container by running this command:

docker-compose run web createdb -h db -U postgres DATABASE_NAME

You can also create your test database the same way. The name of the test
database that Yesod will look for is in your config/test-settings.yml.

Have you ever wanted to to program without nil?
Learn about Haskell's Maybe data type
in our new book
Maybe Haskell
to start living in a null-free world.
You'll find this concept useful whether you program
in Haskell, Scala, OCaml,
or are a heavy user of Swift's Optionals.