## About `cabal install --lib`
**TL;DR: Don't use it, add the library to your *package-name*.cabal
or `package.yaml` instead, or use a [cabal script](https://cabal.readthedocs.io/en/3.10/getting-started.html#run-a-single-file-haskell-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 `cabal`.
Hopeful, you try:
```sh
cabal install fourmolu
```
and, if you are patient, this may well succeed and give you a `fourmolu` executable.
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:
```sh
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 3.10.1.0)
```
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ 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:
```sh
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
:1:3: error:
Variable not in scope: (+) :: t0 -> t1 -> t
ghci> print "hi"
:3:1: error:
Variable not in scope: print :: base-4.17.2.0: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.
## What happened?
Note the line printed by `ghci`:
```
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 `(+)` nor `print`).
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-4.17.2.0` to the aforementioned `default` file.
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](https://github.com/phadej/cabal-extras), 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 ~/.ghc/*architecture*-*OS*-*ghcversion*/environments/default
.
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 `cabal` or `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](https://cabal.readthedocs.io/en/3.10/cabal-project.html)).
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 `build-depends` field.
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](https://cabal.readthedocs.io/en/3.10/cabal-package.html#sublibs), 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](https://cabal.readthedocs.io/en/3.10/cabal-package.html#pkg-field-build-depends) 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](https://docs.haskellstack.org/en/stable/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](https://cabal.readthedocs.io/en/3.10/getting-started.html#run-a-single-file-haskell-script) for more details.