summaryrefslogtreecommitdiff
path: root/haskell/cabal-lib.html
blob: 80be10229e2f53f7645cb8ed9b799497715fe953 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
<h2>About <code>cabal install --lib</code></h2>
<p><strong>TL;DR: Don't use it, add the library to your <code><em>package-name</em>.cabal</code> or <code>package.yaml</code> instead, or use a <a href="https://cabal.readthedocs.io/en/3.10/getting-started.html#run-a-single-file-haskell-script">cabal script</a>. After you learn more about the downsides, you can reconsider. See the &quot;What to do instead&quot; section below.</strong></p>
<hr />
<p>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 <code>fourmolu</code>, and find that installing Haskell packages uses a tool called <code>cabal</code>.
Hopeful, you try:</p>
<pre><code class="language-sh">cabal install fourmolu
</code></pre>
<p>and, if you are patient, this may well succeed and give you a <code>fourmolu</code> executable.</p>
<p>So now you want to write some Haskell!
But you want to use a library, say <code>brick</code>, for making a terminal user interface (TUI).
So you go:</p>
<pre><code class="language-sh">cabal install brick
</code></pre>
<p>which seems to proceed as before, compiling a bunch of dependencies.
(Note that in the past, this <em>was</em> 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)</p>
<pre><code>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: Installation might not be completed as desired! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The command &quot;cabal install [TARGETS]&quot; doesn't expose libraries.
* You might have wanted to add them as dependencies to your package. In this
case add &quot;brick&quot; 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
&quot;cabal install --lib brick&quot;. The &quot;--lib&quot; flag is provisional: see
https://github.com/haskell/cabal/issues/6481 for more information.
</code></pre>
<p>which looks scary, using the same kind of <code>@@@@</code> banner as <code>ssh</code> 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 <code>.hs</code> file and aren't planning on creating a &quot;package&quot;.
So you try the second suggestion:</p>
<pre><code class="language-sh">cabal install --lib brick      # note, don't try this at home
</code></pre>
<p>and that seems to work -- and if you had let the previous <code>cabal install brick</code> command run to completion, it doesn't even seem to do much.
That much is true: it hasn't done much, but what it <em>has</em> done is probably not what you wanted.</p>
<p>For example, let's try to sanity-check our Haskell installation and start a REPL:</p>
<pre><code>$ 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&gt; 1 + 2

&lt;interactive&gt;:1:3: error:
    Variable not in scope: (+) :: t0 -&gt; t1 -&gt; t
ghci&gt; print &quot;hi&quot;

&lt;interactive&gt;:3:1: error:
    Variable not in scope: print :: base-4.17.2.0:GHC.Base.String -&gt; t
ghci&gt;
</code></pre>
<p>I mean, that doesn't look good, does it?</p>
<p>And if that did not scare you enough, suppose that in the future, you want to use a newer version of <code>brick</code> and try to install that using <code>cabal install --lib brick</code> again.
What you'll see is this:</p>
<pre><code>$ 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
</code></pre>
<p>(I simulated the situation by installing an older version instead. I can't time-travel, unfortunately.)</p>
<p>Another thing that would fail is trying to install a package that is incompatible with the versino of <code>brick</code> you have now &quot;installed&quot;.
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.</p>
<h2>What happened?</h2>
<p>Note the line printed by <code>ghci</code>:</p>
<pre><code>Loaded package environment from /home/tom/.ghc/x86_64-linux-9.4.7/environments/default
</code></pre>
<p>This file is the &quot;GHC environment&quot; from above that <code>cabal</code> wrote to.
It now contains this:</p>
<pre><code>clear-package-db
global-package-db
package-db /home/tom/.cabal/store/ghc-9.4.7/package.db
package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
</code></pre>
<p>This means that when starting <code>ghci</code>, these, and no others, are the packages that are in scope:</p>
<pre><code>$ 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&gt; :show packages
active package flags:
  -package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
</code></pre>
<p><em>This does not include <code>base</code>;</em> this is what produced the broken <code>ghci</code> above (which couldn't find <code>(+)</code> nor <code>print</code>).</p>
<p>You can &quot;fix&quot; this:</p>
<pre><code>ghci&gt; :set -package base
package flags have changed, resetting and loading new packages...
ghci&gt; 1 + 2
3
ghci&gt; :show packages
active package flags:
  -package base
  -package-id brick-1.10-1f76dfaf75736c0f6e2a4a2cf992bb12da05f6bbc7985f9787547739947e4696
</code></pre>
<p>and you can even make that change permanent with <code>cabal install --lib base</code>, which adds (in my case) a line <code>package-id base-4.17.2.0</code> to the aforementioned <code>default</code> file.</p>
<p>In short, a <em>GHC environment file</em> was created by <code>cabal</code> that contains a list of packages in scope for <code>ghc</code> when you're not using <code>cabal</code>, or outside the context of a project.
You need to either manage this file manually or through some <a href="https://github.com/phadej/cabal-extras">helper tool</a>, and you will need to, because <code>cabal</code> won't resolve conflicts for you.
You're back to manual, imperative management of dependencies.</p>
<p>Furthermore, this way of installing dependencies is fundamentally separate from the code that <em>uses</em> those dependencies, and there is just <em>one</em> 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.</p>
<h2>How to fix the situation</h2>
<p>If you got yourself in a pickle due to an unintended use of <code>cabal install --lib</code>, you can undo its effects (apart from having used some disk space in compiling the packages in question) by removing the <code>default</code> file mentioned above.
This is in <code>~/.ghc/<em>architecture</em>-<em>OS</em>-<em>ghcversion</em>/environments/default</code>.</p>
<p>As mentioned, the compiled packages are still around (in <code>~/.cabal/store/ghc-<em>version</em>/</code>), but removing those is tricky -- do not try it, cabal likes to maintain its own consistent set of packages in the &quot;store&quot;.
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. :)</p>
<h2>What to do instead</h2>
<p>Create a project!
The intended mode of operation of the modern Haskell tooling, that is <code>cabal</code> or <code>stack</code>, is to always work inside of a <em>project</em>.
Often, &quot;project&quot; basically means &quot;package&quot;, but you can have projects with multiple packages in them (using a <code>cabal.project</code> file, see <a href="https://cabal.readthedocs.io/en/3.10/cabal-project.html">the docs</a>).</p>
<p>Creating a package is easily done using <code>cabal init --simple</code> inside a fresh directory.
If you like to be asked more questions, you can also opt for <code>cabal init</code> instead.
Then, you can <strong>declaratively</strong> add dependencies in the <code>build-depends</code> field of your executable/library in the <code><em>package-name</em>.cabal</code> file that was generated.
Put a comma (<code>,</code>) between the package names in the <code>build-depends</code> field.</p>
<p>Note that if you selected &quot;Library&quot; and &quot;yes&quot; for generating a test suite (the default option), there will be <em>two</em> <code>build-depends</code> blocks in your <code><em>package-name</em>.cabal</code> file, one for each <em>component</em>.
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 &quot;internal library&quot;, for which see <a href="https://cabal.readthedocs.io/en/3.10/cabal-package.html#sublibs">the documentation</a>, but don't worry about that.)</p>
<p>You can start writing code in the <code>app/Main.hs</code> file (or <code>src/MyLib.hs</code> file if you selected Library) and run using <code>cabal run</code> (or build using <code>cabal build</code> in the case of a library).
<strong><code>cabal</code> will automatically ensure that a consistent set of versions is compiled and made available, if at all possible.</strong>
You can also add <a href="https://cabal.readthedocs.io/en/3.10/cabal-package.html#pkg-field-build-depends">version bounds</a> to your dependencies if you want to apply some proper software engineering principles.</p>
<p>(If you want to use <code>stack</code> instead of <code>cabal</code>, try <a href="https://docs.haskellstack.org/en/stable/GUIDE/">their getting started guide</a>.)</p>
<h3>An even lighter-weight alternative</h3>
<p>An alternative to creating a project is to make a <em>cabal script</em>: 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 <a href="https://cabal.readthedocs.io/en/3.10/getting-started.html#run-a-single-file-haskell-script">the documentation</a> for more details.</p>