Choosing a Haskell Code Formatter for Your Team
Your team keeps arguing about where the arrows go in a do block, and every pull request carries twenty lines of whitespace noise next to three lines of real change.

Your team keeps arguing about where the arrows go in a do block, and every pull request carries twenty lines of whitespace noise next to three lines of real change. Picking one Haskell code formatter and enforcing it ends that argument in an afternoon, but only if you choose the tool that matches how your team actually works.
I have moved four codebases onto a single formatter over the last few years, ranging from a 9,000 line internal library to a 140,000 line service monorepo. The choice itself took less time than the rollout, and the rollout is where most teams get hurt. This guide covers both.
The Haskell code formatter options that are still alive
The Haskell ecosystem has gone through several formatters, and some of the names you will find in old blog posts and Stack Overflow answers are no longer maintained. Here is the short list worth evaluating in 2026.
| Tool | Configuration | Scope | Status |
|---|---|---|---|
| Ormolu | Almost none by design | Whole module | Actively maintained |
| Fourmolu | A fourmolu.yaml file with around 20 options | Whole module | Actively maintained fork of Ormolu |
| stylish-haskell | A .stylish-haskell.yaml file | Imports, pragmas, alignment | Maintained, partial formatter |
| Brittany | Config file | Whole module | Archived, avoid for new projects |
| cabal-fmt | Minimal | Only .cabal files | Maintained, pairs with any of the above |
Ormolu came out of Tweag in 2019 with a blunt philosophy: one style, no knobs, and output that never changes the meaning of your code. It parses with the GHC parser itself, so any syntax your compiler accepts, Ormolu can format. It also reads the default extensions from your .cabal file, which matters if you lean on things like OverloadedRecordDot or BlockArguments.
Fourmolu is a fork that keeps the same engine but exposes settings: indentation width (4 by default, versus 2 in Ormolu), leading or trailing commas, where arrows sit in type signatures, how imports are grouped, and whether to respect existing blank lines. If you set every Fourmolu option to match Ormolu, the output is identical.
stylish-haskell is older and does less. It sorts and aligns imports, tidies language pragmas, trims trailing whitespace, and aligns record fields and case branches. Many teams ran it for years alongside manual formatting. Today it mostly makes sense as a bridge for a codebase that is not ready for a full rewrite.
How to choose between Ormolu and Fourmolu
For most teams the real decision is Ormolu or Fourmolu. I use three questions.
Does anyone on the team care deeply about style?
If the honest answer is no, pick Ormolu and move on. Zero configuration means zero future debates. A style file is a magnet for pull requests titled "small tweak to formatting," and each one reformats hundreds of files.
Is the existing code mostly 4 space indented?
Switching a 100,000 line codebase from 4 spaces to 2 touches nearly every line, which wrecks git blame for a long time. Fourmolu with its default 4 space indent can produce a much smaller first diff. On one service I measured it: Ormolu changed 61 percent of lines, Fourmolu with tuned options changed 23 percent.
Do you share code with other open source projects?
Many public libraries on Hackage now ship Ormolu output. If your developers move between your code and those projects every day, matching the common style lowers friction. If you are a closed shop, this matters less.
Wiring it into the editor and the build
A formatter only helps if it runs without anyone thinking about it. Set it up in three places.
- The editor. Haskell Language Server has a formatting provider setting. Set it to ormolu or fourmolu, and set the cabal formatting provider to cabal-fmt. Turn on format on save in your editor. If formatting silently does nothing, the cause is usually an HLS build that does not bundle the plugin you picked; our guide to troubleshooting Haskell Language Server covers how to confirm which plugins your binary includes.
- Pin the version. Formatter output changes between releases. Ormolu 0.7 and 0.5 produce different results on the same file. Pin the exact version in your project, through a cabal.project constraint, a Nix flake, or a tool manifest, and make HLS use that same version. Version drift is the number one cause of "it formats differently on my machine."
- Continuous integration. Both Ormolu and Fourmolu have a check mode that exits with a non-zero status if any file would change. Run it on every tracked .hs file in a job that takes under a minute. On our largest repo the check over roughly 1,800 modules took 38 seconds.
If you are setting up a fresh machine as part of this, the steps in setting up a Haskell development environment already include GHCup and HLS, so adding the formatter is one more line.
Why format on save alone is not enough
The common advice is "just turn on format on save and the problem solves itself." I disagree, and the reason is simple: not everyone uses the same editor, and not every edit goes through an editor. People resolve merge conflicts in a terminal, apply patches from scripts, and accept suggestions in a web review tool. Each of those paths skips your save hook.
Within two weeks of relying only on editor formatting, one team I worked with had 140 unformatted files again. The CI check is what makes the formatter a rule rather than a suggestion. Format on save is the convenience layer. CI is the enforcement layer. You need both.
There is a second, quieter benefit. Once formatting is mechanical, review comments stop being about layout. Reviewers who no longer write "nit: alignment" have more attention for logic, which ties into why code review comments get ignored when they are buried in trivia.
Rolling out a formatter on an existing codebase
The rollout is where teams lose a week if they are careless. Follow this order.
- Agree on the tool and pin the version in one small pull request with no reformatting.
- Pick a quiet moment, ideally right after a release, and ask people to merge or park open branches.
- Run the formatter across the whole repository in a single commit containing nothing else. Write a clear message such as "Apply Ormolu 0.7.7 to all modules."
- Add that commit hash to a .git-blame-ignore-revs file and tell everyone to set blame.ignoreRevsFile in their git config. GitHub and GitLab both read this file automatically in their blame views.
- Turn on the CI check in the same pull request or the one right after.
Long lived branches need special care. The easiest path is for the branch owner to run the same pinned formatter on their branch, commit that, then rebase. Conflicts drop to the lines they actually changed.
Handling the rare bad output
Ormolu supports disabling and re-enabling formatting around a region using special pragmas, which is useful for hand-aligned tables of constants or large literal lists. Use it sparingly. In our 140,000 line repo we ended up with exactly six such regions, all for data tables. If you find yourself adding them weekly, the problem is usually the code shape, not the formatter.
Frequently asked questions
Is Ormolu or Fourmolu better for a new Haskell project?
For a brand new project with no legacy style, Ormolu is the simpler choice because there is nothing to configure or debate. Fourmolu earns its place when you need 4 space indentation or have strong team preferences. Both share the same parsing engine, so correctness is equal.
Can a Haskell code formatter break my code?
Ormolu and Fourmolu check that the formatted output parses to the same syntax tree as the input and refuse to write the file if it does not. In practice this makes semantic breakage extremely rare. CPP heavy modules are the main exception, so test those carefully during rollout.
How do I format .cabal files?
Use cabal-fmt, which sorts and aligns fields, module lists, and dependencies in .cabal files. It is separate from your Haskell formatter, so run both in CI. HLS can call cabal-fmt on save if you set the cabal formatting provider.
More in Developer Tools
Developer Tools
How to Speed Up Cabal Builds on Large Haskell Projects
A clean build of a mid-sized Haskell service can take twelve minutes, and an incremental one after touching a single core...
Developer Tools
Haskell Language Server Troubleshooting for Real Projects
You open a module, the editor shows a spinner for forty seconds, and then every import is underlined in red even though cabal...
Developer Tools
Using GHCi as Your Daily Haskell Workflow
Many Haskell beginners treat GHCi as a calculator they poke at occasionally, then do their real work by compiling full programs...