Lessons on Haskell Package Management

Disclaimer: This particular blog post is intended mostly as a record of how I managed to get package management functioning after fiddling with a Haskell install on a Mac Book Pro. It can also function as a way to help others who have had similar issues and seek resolution, or as a record of how Haskell’s officials have failed to setup new users with an easy path to understanding package management in Haskell.

Installing Haskell

You can install Haskell several ways. This post focuses on the Mac, but hopefully the links will help guide you to a successful install regardless.

The official place for installing Haskell is here.

My first attempt at installing was with the app you can download. I would suggest, however, installing it with Homebrew. Installing Homebrew is a good idea on OSX regardless, as I would say it’s the current de facto package manager.

To install with Homebrew, I ran this command in terminal:

brew cask install haskell-platform

Successful output for this command looked something like this:


==> Satisfying dependencies
complete
==> Downloading https://haskell.org/platform/download/8.0.1/Haskell%20Platform%208.0.1%20Full%2064bit-signed-a.pkg
######################################################################## 100.0%
==> Verifying checksum for Cask haskell-platform
==> Running installer for haskell-platform; your password may be necessary.
==> Package installers may write to any location; options such as --appdir are ignored.
Password:
==> installer: Package name is Haskell Platform 8.0.1
==> installer: Installing at base path /
==> installer: The install was successful.
🍺 haskell-platform was successfully installed!

Cabal, the Devil

First off, let me say that I never got Cabal to work. I think this is partially due to installing different versions of Haskell, and failing in general to understand how cabal exists in relation to ghc and Haskell in general.

I do know, however, that Cabal is not actually a package manager. It’s a build tool. So I am probably not using it properly when just using it to install global dependencies.

Let’s talk a little bit more about what the case was that I was trying to use in the first place.

I had a script that I was trying to run just as a baseline to know I could install dependencies and actually run a Haskell script successfully. The script required importing some non-standard modules, e.g. Aeson for parsing JSON.

So my first attempts to use cabal went like this:


$ cabal install aeson
Resolving dependencies...
All the requested packages are already installed:
aeson-1.0.0.0
Use --reinstall if you want to reinstall anyway.

OK, so it seems Aeson is installed already… let me try to run the script:


$ runhaskell test.hs

test.hs:1:8:
Could not find module ‘Data.Aeson’
Perhaps you meant Data.Version (from base-4.8.2.0)
Use -v to see a list of the files searched for.

 

OK, so my script doesn’t seem to think Aeson is installed, what if I try loading the module through GHCi:


$ ghci
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help
Prelude> :m Data.Aeson

:
Could not find module ‘Data.Aeson’
Perhaps you meant Data.Version (from base-4.8.2.0)

Hmm, GHCi doesn’t seem to think it’s installed either…

Cabal references the same place GHCi and runhaskell does, so I would think they all share the same modules.

Stack, the Savior

After posting about my frustrations, another dev recommended I use stack instead of cabal:


$ stack install aeson
Run from outside a project, using implicit global project config
Using resolver: lts-6.13 from implicit global project's config file: /Users/162084/.stack/global-project/stack.yaml
[1 of 1] Compiling Main ( /private/var/folders/q2/4ljzqf3s1ts65ql7l9bqvdlcw8kf7y/T/stack3958/Setup.hs, /private/var/folders/q2/4ljzqf3s1ts65ql7l9bqvdlcw8kf7y/T/stack3958/Setup.o )
Linking /Users/162084/.stack/setup-exe-cache/x86_64-osx/tmp-setup-Simple-Cabal-1.22.5.0-ghc-7.10.3 ...
fail-4.9.0.0: download
mtl-2.2.1: download
fail-4.9.0.0: configure
dlist-0.7.1.2: download
primitive-0.6.1.0: download
fail-4.9.0.0: build
mtl-2.2.1: configure
fail-4.9.0.0: copy/register
mtl-2.2.1: build
dlist-0.7.1.2: configure
syb-0.6: download
dlist-0.7.1.2: build
primitive-0.6.1.0: configure
primitive-0.6.1.0: build
syb-0.6: configure
syb-0.6: build
dlist-0.7.1.2: copy/register
tagged-0.8.4: download
tagged-0.8.4: configure
mtl-2.2.1: copy/register
tagged-0.8.4: build
text-1.2.2.1: download
text-1.2.2.1: configure
text-1.2.2.1: build
primitive-0.6.1.0: copy/register
syb-0.6: copy/register
vector-0.11.0.0: download
vector-0.11.0.0: configure
tagged-0.8.4: copy/register
vector-0.11.0.0: build
text-1.2.2.1: copy/register
hashable-1.2.4.0: download
hashable-1.2.4.0: configure
hashable-1.2.4.0: build
hashable-1.2.4.0: copy/register
unordered-containers-0.2.7.1: download
unordered-containers-0.2.7.1: configure
unordered-containers-0.2.7.1: build
vector-0.11.0.0: copy/register
scientific-0.3.4.9: download
scientific-0.3.4.9: configure
scientific-0.3.4.9: build
scientific-0.3.4.9: copy/register
attoparsec-0.13.0.2: download
attoparsec-0.13.0.2: configure
attoparsec-0.13.0.2: build
unordered-containers-0.2.7.1: copy/register
semigroups-0.18.1: download
semigroups-0.18.1: configure
semigroups-0.18.1: build
semigroups-0.18.1: copy/register
attoparsec-0.13.0.2: copy/register
aeson-0.11.2.1: download
aeson-0.11.2.1: configure
aeson-0.11.2.1: build
aeson-0.11.2.1: copy/register
Completed 14 action(s).

Whoa, we have this really long output that doesn’t give an error exit code! This looks promising… But again our test scripts give the same error about not finding Aeson.

It’s around this time I uninstall Haskell and re-install it with Homebrew.

I try installing Aeson again with stack, but it complains about the compiler not matching what is expected (I suspect stack expects a different version of ghc than installed via Homebrew). Stack, in the error message, suggests I run stack setup, so I do:


$ stack setup
Run from outside a project, using implicit global project config
Using resolver: lts-6.14 from implicit global project's config file: /Users/162084/.stack/global-project/stack.yaml
Preparing to install GHC to an isolated location.
This will not interfere with any system-level installation.
Downloaded ghc-7.10.3.
Installed GHC.
stack will use a locally installed GHC
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

Notice the last lines:


To use this GHC and packages outside of a project, consider using:
stack ghc, stack ghci, stack runghc, or stack exec

Oh, so maybe I shouldn’t install Aeson with stack and try running the script with runhaskell, I should try running the script with stack runghc?

I installed Aeson with stack, this time successfully, and tried running the script with stack runghc.

And it worked. Finally Aeson was recognized.

Suddenly I realized it seems stack has replaced the normal Haskell tools, which seems confusing as a complete beginner, but made more sense after reading this blog post about how the people in charge of Haskell don’t listen to the community and have failed frequently enough to warrant the community developing their own, independent tools.

I didn’t learn much through this process, other than perhaps it’s best to just use stack and avoid cabal, but I thought I would share my adventure because in the moment I was having such frustration, I was really hoping someone else had shared at least a glimpse of their path of escaping this package-management hell.

Advertisements

2 thoughts on “Lessons on Haskell Package Management

  1. The problem you had with `cabal` is that you almost definitely want to build things in sandboxes for your specific project, or else use the currently being tested `new-build` functionality. That blog post you linked to regarding Stack is *very* one sided (I still very much prefer `cabal` as it’s a lot more flexible – especially for library development – though I recognise the advantages of `stack` in some scenarios); I would argue that it isn’t more a matter of the committee not listening to the community but that a vocal subset of the community – many of whom work together – having their own requirements that were currently un-met by the status quo and being frustrated by the (admittedly often slow) pace of development by the official tooling (which is also developed by the community).

    Like

    1. Hey Ivan,

      I have so little experience with Haskell that I think whatever experience I do have ends up forming my limited perspective – so thank you for taking the time to help expand that perspective. 🙂 Too often people get stuck in flamewars with little evidence being weighed – I find it’s much better to evaluate evidence and move forward, sometimes even resolving to accept diversity.

      I still very much prefer `cabal` as it’s a lot more flexible – especially for library development – though I recognise the advantages of `stack` in some scenarios.

      Do you have any suggested documentation for a beginner that would help them distinguish the cases where cabal or stack would be more or less useful? I would love to learn how to better use cabal, especially after my frustrating experience.

      I would argue that it isn’t more a matter of the committee not listening to the community but that a vocal subset of the community – many of whom work together – having their own requirements that were currently un-met by the status quo and being frustrated by the (admittedly often slow) pace of development by the official tooling (which is also developed by the community).

      Hmm, this is one irony of open source I have noted elsewhere. Community developed software, you would think, would mean the official tools stay relevant to the community’s needs. I think one case where this fracture happens is of course when the official tool doesn’t adopt the community’s changes fast enough (for whatever reason, sometimes it’s political, but often it is more about keeping the tool stable or not veering too much in a different direction from what the majority of users expect from that tool). Either way, it does seem like the most responsible course of action would be to negotiate how the official tools can be updated to suite the sub-community’s needs so that everyone benefits and you don’t have such contentious rifts in the community as a whole.

      However, this depends largely on people being strategic, respectful, and willing to compromise.

      Thank you for your post – I appreciate your counter-examples!

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s