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.
Installing Docker
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.
Installation on OS X
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
- run
brew update && brew install boot2docker docker docker-compose
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:
ln -sfv /usr/local/opt/boot2docker/*.plist ~/Library/LaunchAgents
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.boot2docker.plist
Then add $(boot2docker shellinit 2>/dev/null)
to your shell startup file,
like .bashrc
or .zshrc
.
Installation on Debian
apt-get update && apt-get install docker.io
Diving into Haskell
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.
Setting Up a Yesod Project
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!
Yesod Development with Docker
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
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.
Now, on to our docker-compose.yml
file:
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 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
.
docker-compose run web createdb -h db -U postgres DATABASENAMEtest
That’s all! Now let’s run our app!
docker-compose up
View your app, the default Yesod landing page, in your browser:
Launch on OS X
open http://$(boot2docker ip):3000
Launch on Linux
$BROWSER http://localhost:3000
Now go forth and Haskell!