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
-
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
withoutuninstall
! ↩