TIL: ghc-options: -O2 is a questionable practice

While browsing issues in the cabal github repository I encountered this comment by Oleg Grengrus:

[We] consider ghc-options: -O2 (any -O flag) to be at the very least questionable practice.

In cabal.project you can have

optimization: 2

for all local, or per package (also non-local)

The core idea is that optimizations could be configured for all packages in an uniform way, without investigating if there’s some dev, devel or fast flag.

Also as a bonus, changing optimization level (also possible from command line), won’t cause full recompilation (or worse: mixed-level compilation) as build products are kept separate.

This was news to me, so I thought some people here could also benefit from seeing this.


I do not get why this is a questionable practice.

I have a project that consists of a single Cabal package. In the Cabal manifest we have a flag development and some common stanza magic makes sure that all our code is built:

  • With -O2 when development is false.
  • With -O0 when development is true.

This gives us comfortable development experience, while we still deliver fast programs to our customers.

Is there something wrong with this setup? I understand that we can create two cabal.project files with different optimization but what good would it do in this setting?

1 Like

One advantage of configuring it via cabal.project files is that your build products will be cached separately which means you won’t have to recompile everything when switching optimization levels:

Another advantage is that you don’t have to bother with cabal flags and common stanzas, but that is probably not a big problem for you. And I don’t know if the cabal.project approach is that much simpler.

How does this work? Does Cabal create different subdirectories of dist-newstyle, one for each cabal.project file? This problem can be solved with git worktree which economically creates another work tree for the same repository.

In my case, this problem is solved simply: continuous integration builds the executables for the customers, so I never have to switch the development flag off.

Yeah, it creates a new folder for each optimization level (not one for each cabal.project file).

In your situation it sounds the most sensible to me to have one cabal.project file that you use for CI and one that you use when developing (maybe a cabal.project.local for local development).

How do you manage your flags? If you use cabal configure -f development then you might as well just use cabal configure --disable-optimization, unless the development flag also does other things in addition to disabling optimizations.

1 Like

Yes, it does. For development, we set:

  • ghc-options: -with-rtsopts=-N3 for executable stanzas so that running them leaves some CPU space for other things like HLS or background music.
  • ghc-options: -fdefer-typed-holes -Wtyped-holes for all stanzas so that we can build and run stuff with holes.
  • cpp-options: -DDEVELOPMENT for all stanzas so that the code itself knows which way it is built. (Not sure if this is a good idea but it must have been needed for something.)

However, we can still have all that by setting the flag appropriately in cabal.project.