A middle path for large-scale Haskell refactors

Hand-rolling a refactor across a large Haskell codebase is slow and error-prone. Sed-style replacements are fast but struggle with anything beyond trivial patterns, while full AST manipulation demands deep compiler expertise and often runs slowly. Retrie, now open-sourced, sits between those extremes: it lets developers express rewrites as equations in Haskell syntax itself, then applies them across codebases exceeding one million lines.

The tool can rewrite expressions, types, and patterns, and supports scripting rewrites with side conditions. It respects local scoping, preserves whitespace, and leaves comments untouched. For more advanced transformations, it exposes a library for programmatic use.

How it works

Retrie takes the source-level rewrite target—written just as you would write Haskell—and matches it against the codebase. Because equations are more expressive than regexes, it handles structural patterns that string tools cannot. And because it narrows the candidate search space before parsing, it avoids the slowdown typical of AST-walking tools.

Consider a function that traverses a list twice:

module MyModule where 
foo :: [Int] -> [Int] 
foo ints = map bar (map baz ints)

To combine those traversals into one, you write a rewrite equation and point Retrie at the current directory:

retrie --adhoc "forall f g xs. map f (map g xs) = map (f . g) xs"

Retrie then applies the edit everywhere the pattern matches:

module MyModule where 
foo :: [Int] -> [Int] 
-foo ints = map bar (map baz ints) 
+foo ints = map (bar . baz) ints

Production background

Retrie was built to support Sigma, Facebook’s anti-abuse rule engine written in Haskell. Since migrating Sigma to Haskell in 2015, the team has used Retrie to move rule code onto new APIs and libraries quickly and safely. The tool is now available on GitHub for the broader Haskell community.