Apparently it can be a bit tricky to get some Haskell libraries working on Windows, in which case the Haskell Platform is a great way to get going with Haskell. For Mac and Linux the platform works too, but we can also just grab the latest GHC and Cabal (ooh, shiny!) and go from there.
UPDATE 2019-02: I tend to use ghcup on Mac and Linux these days. Am leaving these steps here as building GHC and Cabal is still a valid way of getting Haskell up and running.
This is how I got it working on my Mac, with loads of help from ddere and bitemyapp on the #haskell-beginners channel on Freenode IRC. It is reasonable to assume all mistakes in this write up are mine, while they deserve the credit for any useful bits.
I’ve got XCode 5.1.1 installed, which I believe is a prerequisite (or at least the dev tools?). Other than that, grab a terminal and a browser, and we’re set to go.
tl;dr
UPDATE 2019-02: Check out ghcup for an easier way to get a platformless Haskell running on Mac or Linux. I’ve switched to using that to manage Haskell installations.
Here’s the summary if you want the steps without explanation:
- Grab the binary GHC distribution, extract,
configure --prefix=<my-dir>
,make install
, and add to PATH - Grab the Cabal binary and add it to the GHC
bin
directory cabal update; cabal install cabal cabal-install alex happy
- Add
~/.cabal/bin/
to PATH - Build projects in a sandbox (
cabal sandbox init
) - Build binaries in a sandbox and symlink or copy to
~/.cabal/bin
; or install directly into~/.cabal
andrm -rf ~/.ghc
if we ever get build conflicts. I’m doing the former.
The rest of the post will go through the specific commands used, and explain some of the decisions you might need to make.
Installing GHC
- Grab the latest binary distribution of GHC and extract it somewhere (I used
~/dev/ghc-7.8.2
) - Open a terminal and run
./configure --prefix=<my-dir>
from the extract directory. I used./configure --prefix=/Users/dave/dev/ghc
. make install
- Next I added the GHC binaries to my PATH. That’s
~/dev/ghc/bin
for me.
We should now be able to run ghc
, ghci
and co. Success!
Bootstrap cabal-install binary
- Grab the latest cabal-install binary.
- Extract it and copy the
cabal
binary somewhere. I put mine in alongside my GHC binaries in~/dev/ghc/bin
so it is on my PATH and I can quickly fallback to it if I nuke everything else but GHC. - Run
cabal update
to initialise the package database.
This will just be used to kick off our cabal-ing. Afterwards we’ll be managing cabal with cabal (for that nice recursive touch).
Final bits and pieces
We’re now going to build and install some final bits and pieces into Cabal’s user-db (stored in ~/.cabal/
).
% cabal install cabal cabal-install alex happy
Next up I adjusted my PATH to make sure binaries are loaded from ~/.cabal/bin
first1. My PATH now looks like this:
export PATH=~/.cabal/bin:~/dev/ghc/bin:(non-haskell stuff)
New projects
We should now have everything we need to build Haskell projects. For projects we’ll run all our cabal install
commands within a sandbox.
% mkdir myNewProj
% cd myNewProj
% cabal sandbox init
% cabal init
-- insert joyous haskelling here --
Moar binaries!
Sometimes we’d like to use cabal
to install some binaries like hlint
, hoogle
or pointfree
. I’ve heard a few schools of thought on this.
Sandboxed builds
Here is what I’ve found works reasonably well for me. I’ve created a directory ~/dev/hs/
to build these utilities in. From there:
~/dev/hs/ % mkdir hlint
~/dev/hs/ % cd hlint
~/dev/hs/hlint % cabal sandbox init
~/dev/hs/hlint % cabal install hlint
~/dev/hs/hlint % ln -s "$(pwd)/.cabal-sandbox/bin/hlint" ~/.cabal/bin/
This builds gives us a fresh hlint
binary and creates a symbolic link to it in the .cabal/bin
directory (i.e. somewhere on my PATH). Sometimes I’ll copy instead of symlink.
The good thing about this is if I need to use specific versions of a particular dependent library for a build I can cabal install
it without worrying about it affecting other builds outside the sandbox.
The catch is some libraries also link against static assets that get put in $(pwd)/.cabal-sandbox/share
, which means if we move or delete this sandbox that binary will stop working.
In user-db
The other approach is to cabal install
the utility outside of a sandbox. This means all docs and static assets go into a safe location (~/.cabal
), but on the downside we’ll sometimes get build failures due to library version conflicts.
In these cases we need to delete everything in ~/.ghc
and try again. I have it on good authority from several sources that this is no problem. All our binaries in ~/.cabal
should still work, it just means next cabal install
won’t rely on cached library builds.
Still, I feel more comfortable with the sandboxed build approach (almost definitely because I don’t fully understand what’s going on behind the scenes).
Pandoc example
At the time of writing I had some trouble building the wonderful Pandoc library due to a change in a dependent library. Pandoc is a library that relies on statically linked assets by default which was mentioned in the sandboxed builds section as a possible problem. Thankfully it provides a build option to embed these assets.
% cd ~/dev/hs
% mkdir pandoc
% cd pandoc
% cabal sandbox init
% cabal install exceptions-0.4
% cabal install hsb2hs
% cabal install pandoc -fembed_data_files
% cp "$(pwd)/.cabal-sandbox/bin/" ~/.cabal/bin/
Installing a specific version of exceptions-0.4
fixed the build problem, while passing the -fembed_data_files
option to the Pandoc build embeds the static assets so we can move the binary and delete the sandbox without breaking Pandoc.
Thanks to Carter for telling me which version of exceptions
I needed, and about -fembed_data_files
for Pandoc.
Request for corrections
This seems to be working ok for me, but if you can see any problems with this approach or can suggest any improvements please let me know and I’ll update the post.
We’ve now installed a verion of the
cabal
binary into~/.cabal/bin
. By putting that into our PATH first we’ll always use the latest version for our builds. If we lose our~/.cabal
for some reason then we can fall back to the one we put into theghc
folder earlier.↩