cabal install --lib
TL;DR: Don't use it, add the library to your
package.yaml instead, or use a cabal script. After you learn more about the downsides, you can reconsider. See the "What to do instead" section below.
Suppose you are new to Haskell, or at least new to the current (2023) Haskell tooling, and would like to install a program written in Haskell.
For example, say you would like to install a Haskell formatter, say
fourmolu, and find that installing Haskell packages uses a tool called
Hopeful, you try:
cabal install fourmolu
and, if you are patient, this may well succeed and give you a
So now you want to write some Haskell!
But you want to use a library, say
brick, for making a terminal user interface (TUI).
So you go:
cabal install brick
which seems to proceed as before, compiling a bunch of dependencies. (Note that in the past, this was a common way to install Haskell libraries for use in your own code, and quite a number of READMEs of older libraries still recommend this command.) But at the end it prints this warning: (as of cabal-install 22.214.171.124)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: Installation might not be completed as desired! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ The command "cabal install [TARGETS]" doesn't expose libraries. * You might have wanted to add them as dependencies to your package. In this case add "brick" to the build-depends field(s) of your package's .cabal file. * You might have wanted to add them to a GHC environment. In this case use "cabal install --lib brick". The "--lib" flag is provisional: see https://github.com/haskell/cabal/issues/6481 for more information.
which looks scary, using the same kind of
@@@@ banner as
ssh reporting a possible man-in-the-middle attack (a changed host key, really).
But you want to use this library, after all, and you're just working in a single
.hs file and aren't planning on creating a "package".
So you try the second suggestion:
cabal install --lib brick # note, don't try this at home
and that seems to work -- and if you had let the previous
cabal install brick command run to completion, it doesn't even seem to do much.
That much is true: it hasn't done much, but what it has done is probably not what you wanted.
For example, let's try to sanity-check our Haskell installation and start a REPL:
$ ghci Loaded package environment from /home/tom/.ghc/x86_64-linux-9.4.7/environments/default GHCi, version 9.4.7: https://www.haskell.org/ghc/ :? for help ghci> 1 + 2 <interactive>:1:3: error: Variable not in scope: (+) :: t0 -> t1 -> t ghci> print "hi" <interactive>:3:1: error: Variable not in scope: print :: base-126.96.36.199:GHC.Base.String -> t ghci>
I mean, that doesn't look good, does it?
And if that did not scare you enough, suppose that in the future, you want to use a newer version of
brick and try to install that using
cabal install --lib brick again.
What you'll see is this:
$ cabal install --lib brick-1.9 Error: cabal: Packages requested to install already exist in environment file at /home/tom/.ghc/x86_64-linux-9.4.7/environments/default. Overwriting them may break other packages. Use --force-reinstalls to proceed anyway. Packages: brick
(I simulated the situation by installing an older version instead. I can't time-travel, unfortunately.)
Another thing that would fail is trying to install a package that is incompatible with the versino of
brick you have now "installed".
I don't have a good example for this post because I couldn't find a neat pair of incompatible packages that didn't have many other dependencies, but I hope you'll trust me that this will result in the well-known (to seasoned haskellers) cabal dependency resolution errors.
Note the line printed by
Loaded package environment from /home/tom/.ghc/x86_64-linux-9.4.7/environments/default
This file is the "GHC environment" from above that
cabal wrote to.
It now contains this:
clear-package-db global-package-db package-db /home/tom/.cabal/store/ghc-9.4.7/package.db package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
This means that when starting
ghci, these, and no others, are the packages that are in scope:
$ ghci Loaded package environment from /home/tom/.ghc/x86_64-linux-9.4.7/environments/default GHCi, version 9.4.7: https://www.haskell.org/ghc/ :? for help ghci> :show packages active package flags: -package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
This does not include
base; this is what produced the broken
ghci above (which couldn't find
You can "fix" this:
ghci> :set -package base package flags have changed, resetting and loading new packages... ghci> 1 + 2 3 ghci> :show packages active package flags: -package base -package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
and you can even make that change permanent with
cabal install --lib base, which adds (in my case) a line
package-id base-188.8.131.52 to the aforementioned
In short, a GHC environment file was created by
cabal that contains a list of packages in scope for
ghc when you're not using
cabal, or outside the context of a project.
You need to either manage this file manually or through some helper tool, and you will need to, because
cabal won't resolve conflicts for you.
You're back to manual, imperative management of dependencies.
Furthermore, this way of installing dependencies is fundamentally separate from the code that uses those dependencies, and there is just one such global list. So if you have multiple Haskell programs that you'd like to work globally, outside of a project, their dependency sets had better be compatible, or things will break.
How to fix the situation
If you got yourself in a pickle due to an unintended use of
cabal install --lib, you can undo its effects (apart from having used some disk space in compiling the packages in question) by removing the
default file mentioned above.
This is in
As mentioned, the compiled packages are still around (in
~/.cabal/store/ghc-version/), but removing those is tricky -- do not try it, cabal likes to maintain its own consistent set of packages in the "store".
Removing the entire store folder for a particular GHC version is safe, however -- even though this does of course mean that you may need to recompile a lot of things later. :)
What to do instead
Create a project!
The intended mode of operation of the modern Haskell tooling, that is
stack, is to always work inside of a project.
Often, "project" basically means "package", but you can have projects with multiple packages in them (using a
cabal.project file, see the docs).
Creating a package is easily done using
cabal init --simple inside a fresh directory.
If you like to be asked more questions, you can also opt for
cabal init instead.
Then, you can declaratively add dependencies in the
build-depends field of your executable/library in the
package-name.cabal file that was generated.
Put a comma (
,) between the package names in the
Note that if you selected "Library" and "yes" for generating a test suite (the default option), there will be two
build-depends blocks in your
package-name.cabal file, one for each component.
A package (a single thing in the package repository, should you decide to upload it to Hackage at some point) can contain multiple components: possibly one public library, as well as zero or more executables, test suites, or benchmarks.
(There is also the concept of an "internal library", for which see the documentation, but don't worry about that.)
You can start writing code in the
app/Main.hs file (or
src/MyLib.hs file if you selected Library) and run using
cabal run (or build using
cabal build in the case of a library).
cabal will automatically ensure that a consistent set of versions is compiled and made available, if at all possible.
You can also add version bounds to your dependencies if you want to apply some proper software engineering principles.
(If you want to use
stack instead of
cabal, try their getting started guide.)
An even lighter-weight alternative
An alternative to creating a project is to make a cabal script: this allows you to effectively make a self-contained project inside a single Haskell file. You specify the dependencies in a special comment block at the top of the file. See the documentation for more details.