Multiple components in ghci is finally here

Loading multiple components in ghci is finally possible with ghc 9.4 and cabal 3.12. They have been available for a while both in nixos-2411 and ghcup. There is no reason not to use it.

This is a big deal because it directly affects the length of the feedback loop. I have been writing about it in the past. And I do think that a short feedback loop is the key to a good productivity.

Back to the story, until recently, ghci were unable to load multiple components, say an executable, like a test suite for example, and the library it depends on at the same time. And ghci is the centerpiece to get the fastest feedback loop when working with Haskell. Until now, working on a feature usually consists in doing some kind of back and forth between working on the library that contains the actual code and working on the test suite to make sure it works, restarting ghci between each iteration. This is far from being ideal and makes the feedback loop way longer than it needs to be.

That’s only a concern for cabal users by the way. stack have had a workaround for this for a while (see the documentation for stack ghci). What it does is merging all ghc options of the components before loading them in ghci. Basically, it is like creating a fake component which is the union of all wanted components. Pretty basic, but it has been an effective workaround for this issue.

But now it is possible to do it as well in cabal. At last, hurrah 🎉

Now, how does that work in practice?

The first thing is of course to have at least the versions of ghc 9.4and cabal 3.12.

Then cabal must be configured to enable the feature. When trying to load two components with cabal. It outputs a pretty informative message:

$ cabal repl test:my-app-tests lib:my-app
Error: [Cabal-7076]
Cannot open a repl for multiple components at once. The targets 'my-app' and 'tests' refer to different components..

Your compiler supports a multiple component repl but support is not enabled.
The experimental multi repl can be enabled by
  * Globally: Setting multi-repl: True in your .cabal/config
  * Project Wide: Setting multi-repl: True in your cabal.project file
  * Per Invocation: By passing --enable-multi-repl when starting the repl

As for me, I just turn it on globally. It is just too useful.

Once cabal is configured, that’s it really. If you try again that last command, that should work. Then after you edit a file in any of the loaded components, all required files will be recompiled by a simple :r in ghci.

The feedback loop I explained here works out of the box. Interestingly, it seems that it sometime makes ghc output the absolute path of the files that contains errors. This workaround is thus not always needed which is also awesome.

One caveat though, ghcid is able to run a command after the compilation succeeds. It is super useful to run the tests on any change for example. Let’s try this:

$ ghcid -c "cabal repl test:my-app-tests lib:my-app -T :main"
Command is not supported (yet) in multi-mode

Oh no, that doesn’t work.

But there is a way to work around this. I learned it in this video (recommended watch to see the feature in action). Instead or running a command, we’ll just execute the main. For example, if the main function is in the Main module, we can do this:

$ ghcid -c "cabal repl test:my-app-tests lib:my-app" -T Main.main

This works fine. Watch out that the executable must come first in the list of the components for some reason. If one needs to pass arguments to the main function, it is possible to use the :set command in ghci.

For example:

$ ghcid -c "cabal repl test:my-app-tests lib:my-app" -s ":set args -p /testpattern/" -T Main.main

That’s it. Once one tried it, it is hard to believe we have been waiting for so long for this.

Note: cabal documentation

March 24, 2025