Use Markdown changelog entry
[cabal.git] / doc / internal / multi-repl.md
blob1e89872886a3564a06ed3018c28aa614c4468149
1 ---
2 author: matthew
3 title: Multiple Component support for cabal repl
4 postName: cabal-multi-unit
5 categories: cabal, ghc, hls, hasura, open-source
6 showtoc: true
7 ---
9 Following on from [our work implementing support for compiling multiple units
10 at once in GHC](https://well-typed.com/blog/2022/01/multiple-home-units/), we have now been extending the ecosystem to take
11 advantage of this new support. This work has once again been made possible by
12 [Hasura](https://hasura.io/). This work continues our productive and
13 [long-running
14 collaboration](https://well-typed.com/blog/2022/05/hasura-supports-haskell-tooling/)
15 on important and difficult tooling tasks which will ultimately benefit the
16 entire ecosystem.
18 This post focuses on updates to the `cabal repl` command, allowing
19 multiple components to be loaded at once into an interactive session. The work is being
20 reviewed in [Cabal MR #8726](https://github.com/haskell/cabal/pull/8726), and should
21 be available in a future release of `cabal-install`.
23 <!-- more -->
25 # Multiple Component Repl
27 When using `cabal`, most commands take a "target" which specifies which units you want
28 to operate on. A command such as `cabal build <target>` will resolve all the units that
29 the target `<target>` resolves to, and build all of them. The behaviour of the `cabal repl`
30 command is different: you must specify a single unit to build.
32 Here are some common targets which you can specify when using `cabal`.
34 * `all`: Build all the locally defined components.
35 * `exe:haskell-language-server`: Build the executable called `haskell-language-server`
36 * `lib:pkg-a lib:pkg-b`: Build the local libraries pkg-a and pkg-b. pkg-a will be the active unit.
37 * `src/Main.hs`: Build the unit which `src/Main.hs` belongs to.
39 After enabling multi-repl, passing a target specification to `cabal repl` which
40 resolves to multiple units will load all those units into a single repl session.
41 The first "target" will be the active unit.
42 For example:
44 ```
45 cabal repl --enable-multi-repl lib:pkg-a lib:pkg-b
46 ```
48 When the modules are compiled, the unit which they came from is listed next
49 to the module name. The `interactive` herald in the build plan indicates that
50 the library will be loaded into GHCi rather than being built like a normal package.
52 ```
53 In order, the following will be built (use -v for more details):
54  - pkg-a-0 (interactive) (lib) (first run)
55  - pkg-b-0 (interactive) (lib) (dependency rebuilt)
56 Preprocessing library for pkg-a-0..
57 Preprocessing library for pkg-b-0..
58 GHCi, version 9.4.3: https://www.haskell.org/ghc/  :? for help
59 [1 of 2] Compiling Foo[pkg-a-0-inplace]
60 [2 of 2] Compiling Bar[pkg-b-0-inplace]
61 Ok, two modules loaded.
62 ```
64 You will need to use at least `ghc-9.4.1` in order to use multiple unit support.
65 It's advised to use `ghc-9.4.5` or `9.6.1`, in order to benefit from bug fixes.
67 ## Enabling Multi-repl
69 There are three ways to enable the multi-repl depending on how much you like it:
71 * Globally: Add `multi-repl: True` to your `~/.cabal/config` file.
72 * Project-wide: Add `multi-repl: True` to your cabal.project file.
73 * Per-invocation: Pass `--enable-multi-repl` when invoking `cabal repl`.
75 A future cabal version is likely to enable multi-repl by default. For the time being,
76 and due to the experimental nature of the command and lack of support in ghci for some features,
77 the multi-repl feature is opt-in.
79 # Closure Property for Multiple Home Units
81 For tools or libraries using the GHC API there is one very [important closure property](https://well-typed.com/blog/2022/01/multiple-home-units/#closure-property-for-home-units)
82 which must be adhered to:
84 > Any dependency which is not a home unit must not (transitively) depend on a home unit.
86 For example, if you have three units `p`, `q` and `r`, and `p` depends on `q` which depends on `r`, then it
87 is illegal to load both `p` and `r` as home units but not `q`, because `q` is a dependency of the home unit `p` which depends
88 on another home unit `r`.
90 `cabal` will automatically enable loading of all units which are needed by the closure
91 property (including non-local) packages. Given the previous example, if you specify
92 on the command line `cabal repl lib:p lib:q` then `lib:r` will also be loaded
93 into the same session as it is needed for the closure property.
95 # Configuring and Promised Dependencies
97 The lowest-level interface which the `Cabal` library provides in order to build a package
98 is the [`Setup.hs` script](https://cabal.readthedocs.io/en/3.10/setup-commands.html).
99 This consists of a normal Haskell file which depends on the `Cabal` library and can be executed
100 in order to build the package. This is done, after compiling `Setup.hs`, via the following invocations:
103 ./Setup configure
104 ./Setup build
107 The `configure` phase checks to make sure that everything is in order so that when
108 the build phase is run we know that all the environmental dependencies have already
109 been provisioned by the user.
111 In the very old days, people would compile and run `Setup.hs` themselves in order to
112 build a package, but these days, all the interactions with `Setup.hs` are managed by a
113 higher-level build tool such as `cabal-install`, `stack` or `nix`. All of these tools
114 ultimately call `Setup.hs` scripts.
116 The main technical change to enable the multi-repl was to modify the `Setup.hs`
117 scripts to allow you to configure a package before all its dependencies are
118 built. Now you can **promise** to `Setup.hs`
119 that a certain dependency will be built by the time we attempt to build the unit. Since
120 all units in a project are going to be built at the same time with one GHC invocation, they
121 all need to be configured before anything else is built. So we just **promise** that all local
122 packages will be built.
125 ./Setup configure --promised-dependency=pkg-a
128 In addition to the `configure` and `build` commands, `Setup.hs` also provides a `repl`
129 command which starts `GHCi` and loads a single component.
132 ./Setup repl
135 This design is quite awkward because the `Setup.hs` scripts operate on a per-component basis. The
136 interface is not aware of the existence of complicated multi-component projects, that is solely the
137 domain of higher-level tools like `cabal-install`. Therefore, instead of starting the repl from
138 the `Setup.hs` script, we need to start a multi-repl from `cabal-install`. However, the `Setup.hs`
139 script is still responsible for computing the arguments we need to pass to GHC in order to compile
140 that component. The solution is to allow the `repl` command to write its arguments into a file
141 so that they can be collected later by `cabal-install` to correctly start a multi-component session.
144 ./Setup repl --repl-multi-file=multi-args
145 # Arguments can be found in the `multi-args` directory.
148 This allows all the units in your project to be configured before any of them are built.
149 After a project is configured, the `Setup` executable can be consulted to find out what
150 options GHC **would** use to build the unit, and because we have **promised** to
151 make sure things are built in the right order, we can supply these options to GHC
152 in order to start a multi unit GHCi session.
154 # HLS support for multiple home units
156 Zubin has already updated HLS to use native multiple home unit support for GHC-9.4.
158 The missing piece has been a mechanism to set up a multi component session which
159 satisfies the closure property. Without such a mechanism, HLS would construct a multiple component session
160 incrementally by adding units to a session as they are opened by the user. For a complicated
161 project structure, users would have to very carefully load their files in the right order to
162 get a session which worked correctly.
163 Even worse, this doesn't even work when a non-local package is needed to satisfy the
164 closure property.
166 HLS consults cabal in order to set up a session: it invokes `cabal repl`
167 and intercepts the final call to `ghc` which would start the repl. That command is then
168 used as the options which are needed for the session in order to compile that unit.
170 Now that `cabal repl` supports creating a command line which specifies the options
171 for multiple components at once, it makes sense to augment the HLS session loading logic
172 to also understand these command lines in order to set up a whole multi-component session
173 at once.
175 HLS now can understand and parse the kind of command line produced by a multiple
176 component session. As a result:
178 * The correct session is initialised up-front. Loading any component in your
179   local project will work seamlessly and quickly.
180 * The time taken to initialise a session is reduced, because no local dependencies
181   are built before the session is started. All local components are configured
182   before anything is built.
183 * Cabal ensures the closure property holds, even for non-local packages.
185 I have been testing this support when working on `cabal` and `ghc`, both projects
186 with many local dependencies and the experience is much improved. In particular for
187 `cabal`, the non-local `hackage-security` package is needed for the closure property but could
188 never be loaded before. This made using HLS on `cabal` very error-prone because if
189 you opened a file from the `Cabal` library and `cabal-install` library, you would
190 break the session without a way to recover it. For `ghc`, it is a lifeline to be able to
191 edit packages like `template-haskell` and see the changes ripple upwards through all
192 the boot libraries and compiler.
194 # Limitations
196 Now that there is a way to easily create and invoke a multi-repl session,
197 users are probably going to run into limitations of the multi-repl.
199 Many features are not yet implemented because there is not a good way to change what
200 the "active unit" of the repl session is. Some more careful thinking needs to be done
201 to modify the GHCi interface in order to work nicely with multiple components in all situations.
203 At this time, the multi-repl is best used for interactive development situations where
204 you want to use the repl to obtain fast-feedback about your project.
205 We have made sure that the multi-repl works with `ghcid` for example.
207 When evaluating code, make sure that the code is in the scope of the active unit,
208 which is the first target given on the command line. For example, to run the test suite
209 entrypoint, use:
212 ghcid --command "cabal repl --enable-multi-repl test:suite lib:pkg" --test Main.main
215 # Conclusion
217 Adding `cabal repl` support for multiple home units allows developers to easily
218 interact with multiple home unit support in GHC. There are still limitations to
219 the repl supported in multiple unit sessions, but as more users start using and wanting this
220 feature we hope to expand the repl to work properly with multiple home units as well.
222 Well-Typed is able to work on GHC, HLS, Cabal and other core Haskell
223 infrastructure thanks to funding from various sponsors. If your company might be
224 able to contribute to this work, sponsor maintenance efforts, or fund the
225 implementation of other features, please
226 [read about how you can help](/blog/2022/11/funding-ghc-maintenance) or
227 [get in touch](mailto:info@well-typed.com).