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:
- Install VirtualBox
brew update && brew install boot2docker docker docker-compose
Great! Now start boot2docker by running
boot2docker init then
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
export statements again you can run
boot2docker shellinit and
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:
$ ln -sfv /usr/local/opt/boot2docker/*.plist ~/Library/LaunchAgents $ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.boot2docker.plist
$(boot2docker shellinit 2>/dev/null) to your shell startup file,
apt-get update && apt-get install docker.io
docker run --rm --interactive --tty thoughtbot/ghc
--rm disposes of the container when we’re done while
--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
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.
To quickly setup a new Yesod project, we provide a yesod-bin Docker image
which gives you easy access to the
yesod command. Make and navigate into a
new project directory.
mkdir myproj && cd myproj
Then run this command:
docker run --rm --interactive --volume $PWD:/src thoughtbot/yesod-bin init --bare
This command mounts your working directory to the
/src directory in the
container and then runs
yesod init --bare. Follow the prompts to create a new
project and voilà, that’s it!
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.
First, let’s create the
Dockerfile using the Yesod image as a base:
FROM thoughtbot/yesod RUN mkdir -p /app WORKDIR /app COPY *.cabal ./ RUN cabal install --dependencies-only -j4 --enable-tests
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.
Now, on to our
db: image: postgres:9.4 ports: - "5432" web: build: . command: yesod devel environment: - HOST=0.0.0.0 - PGUSER=postgres - PGPASS - PGHOST=db stdin_open: true volumes: - .:/app ports: - "3000:3000" links: - db
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
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
command), mount our current directory to the container’s
/app folder, map the
port 3000, and finally link the
db container to the
Almost there! All that’s left now is to create your database. If you look at
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
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
docker-compose run web createdb -h db -U postgres DATABASE_NAME_test
That’s all! Now let’s run our app!
View your app, the default Yesod landing page, in your browser:
open http://$(boot2docker ip):3000
Now go forth and Haskell!