Why I am not a fan of Cabal or Stack

It’s 2018. We still have cabal install but no cabal uninstall. This is not okay; it has never been okay; it will never be okay; no reasonable person would be okay with this; in no universe is this okay.

This is not, however, the reason for my most recent trip to Cabal hell. No, my most recent Cabal issue was refreshingly straightforward: running cabal update caused any subsequent cabal list to crash — with an error message “helpfully” suggesting that cabal update might fix things. A tool should not break itself!

After some investigation, cabal update appeared to have downloaded a file cabal list could not parse. This is silly. Cabal should verify the file parses after downloading it and throw it away (with a warning) if it doesn’t! Moreover, the error cabal list produced was:

internal error when reading package index: failed to parse .cabal file

This message is useless garbage. I don’t have a .cabal file. I do have a ~/.cabal directory, so presumably the offending “package index” is somewhere in there. Where? God only knows.

It is at this point I lost patience and nuked ~/.cabal for the nth time.1

If Cabal is hell, Stack is purgatory

The leading alternative to Cabal is probably Stack. Quoth the Stack guide:

stack is a modern, cross-platform build tool for Haskell code. [...]
stack has also been designed from the ground up to be user friendly, with an intuitive, discoverable command line interface.

Lovely! I’ll just run stack help to get my bearings, then...

$ stack help
Invalid argument `help'
Auxiliary command not found in path `stack-help'
File does not exist or is not a regular file `help'
[... et cetera ...]

This is dumb. Obviously I want help. Apparently the right thing to do is:

$ stack --help

But this produces multiple pages of output, so I have to pipe it to less to read it. It should do this automatically! If you’re not going to have a man page (and Stack doesn’t), your help command must be at least as usable as a man page.

Moreover,

$ stack --help init

does not produce help for the init command. This is dumb. Obviously I want help for the init command. Stack should learn from git here — each of the following should work and do the same thing: stack help init; stack --help init; and stack init --help.

Give me hypermedia or give me death

Even once you stumble upon the correct incantation, Stack’s help is quite vague:

$ stack init --help
Usage: stack init [DIRS] [--solver] [--omit-packages] [--force]
                  [--ignore-subdirs] [--help]
  Create stack project config from cabal or hpack package specifications
[... et cetera ...]

Erm, what does it do?

“Create stack project config from cabal or hpack package specifications”?

I have no idea what this means. I came to Stack to avoid Cabal. Every interaction I’ve had with Cabal has been a combination of mystifying and frustrating. I don’t ever want to hear the word “Cabal” again if I can avoid it. Don’t make me understand the crappy tool you replace in order to use your new, better one!

Besides which, this explains nothing. What is a Stack project config? What is a Cabal/hpack specification? When and why would I want to turn one into the other, and how does stack go about doing it? If you claim to be friendly to new users, you must answer these questions. At a bare minimum, point the user to a reference which does answer these questions — after all, man pages can reference other man pages, and web pages can link to other web pages. Documentation should be hyperlinked!

At least Stack can uninstall, right?

Well, about that...

$ stack install --help
[... help for 'stack install' ...]

$ stack uninstall --help
Usage: stack uninstall [IGNORED] [--help]
  DEPRECATED: This command performs no actions, and is present for documentation
  only

Having install without uninstall is still unforgivable.

Yes, I know stack isn’t really a package manager; and yes, I know stack install is just an alias for stack build --copy-bins, which blindly copies a bunch of binary files into ~/.local/bin. None of this is a good excuse. If stack isn’t a package manager, it shouldn’t pretend to be one by having an install command lying around like a loaded footgun.

Isn’t it about time Haskell had a decent package manager, anyway? It’s 2018!

Projects are an unnecessary barrier to entry

Stack assumes that if you want to use it, the first thing you’ll do is set up a “project” using stack new. This makes me grumble. I shouldn’t need a “project” with a fixed directory structure just to get up and running. I don’t want to jump through your arbitrary hoops. I have a single goddamn file that needs a particular library. I want to install it, with the ability to uninstall or upgrade or replace it later, and move on with my life.

I’ll make a project when the thing I’m building needs a project, which is probably never, because most of my code is throwaway code exploring interesting concepts, because I’m an academic and a hobbyist. Hell, I suspect most code is throwaway code anyway. (Moreover, every “project” structure I’ve ever seen has way too many goddamn files.)

For a language ecosystem which gets this mostly right, try Racket.

Footnotes

  1. I eventually got the crash bug fixed by prodding some Cabal folks on IRC. They were very helpful and polite and I really don’t want to rag on their hard work, but the fact that this bug could happen at all is frustrating. It’s indicative of architectural flaws: verify what you download! have useful error messages! and for Christ’s sake, don’t have install without uninstall!