Use Hpack, and you don’t have to manually add/remove modules in the cabal file’s exposed-modules
and other-modules
section!
Haskell has a very long history: Since 1987 which is long before the concept of “package managers.” When modern package management emerged, Haskellers had to invent something too. So they made “Cabal” which came to mean three things:
(This is why you should be careful about what someone means when they say “Cabal.”)
And then there is cabal-install
which is different from “Cabal.” cabal-install
is a command line application that lets you manage Cabal-based projects.
So, we had commands like ghc
, ghci
, and cabal-install
, and we were happier than before. Until the programming community realized the problem of dependency hell; which package depends to which version of which library, etc. Naturally, we needed some kind of sandboxing per project, so we added that to cabal. That’s called Cabal sandbox. The problem was, although Cabal sandbox did solve the problem of dependencies-per-project,
Stack solved these problems. Stack is actually a wrapper (plus some great deal of its own machinery) around the commands like ghc
, ghci
, and cabal-install
. It does a brilliant job to isolate everything (both compilers and dependency packages) and sharing a build result if it can be shared.
For example, if you already have a bytestring-0.10 that was built using base-4.8, you don’t have to build it again when it’s needed because it’s cached. However, when you need base-4.9 and bytestring-0.10 in your project, you’ll have to build bytestring from scratch; this is exactly the behavior we want.
One of the remaining problems is that the <projectname>.cabal
file is in a custom format. Custom formats are generally not good because you can’t leverage the existing tools and free-ride other people’s work. Another pain point of the cabal files is that, whenever you add or remove some module (Haskell source code file) to/from your project, you have to edit the exposed-modules
or other-modules
section of the cabal file. Tedious, error-prone, and not so rewarding.
We understand that the concept of cabal file and its format came out before the popularization of YAML or TOML or anything, and we also understand that if Cabal decided to switch from <projectname>.cabal
to package.yaml
, it would be a breaking change. But maybe we can write some code to
package.yaml
fileexposed-modules
/other-modules
problem.Fortunately, someone already did this. It’s called Hpack.
Use Hpack.
Hpack is built into Stack, so there is no additional setup required. This is so brilliant.
If you already have a Haskell project, use hpack-convert. It will automatically generate a package.yaml
file from your <projectname.cabal>
file.
If you are starting a new Haskell project, use Haskeleton. It’s simple:
stack new myprojectname haskeleton -p 'github-username:xtendo-org' -p 'author-name:XT'
Actually, my truly favorite thing about hpack is that you can specify a common set of dependencies that get shared across all the libraries/exes/tests/benchmarks in your setup. That part is truly annoying in cabal files.
You can do the same thing with ghc-options, language extensions, etc.
I totally agree.