Facebook has open-sourced the tool they use to refactor their own Sigma anti-abuse rule engine. Named Retrie, this tool allows developers to describe rewrites as equations in Haskell syntax rather than regular expressions.
Retrie is aimed at making refactoring Haskell code faster, easier, and safer, Facebook says.
Retrie’s features include the ability to rewrite expressions, types, and patterns; the ability to script rewrites and add side conditions; and a library for scripting more advanced rewrites. Retrie also respects and maintains local scoping, preserves white space, and does not rewrite code comments.
Retrie attempts to strike a balance between text-oriented refactoring tools, which are fast but lack expressive power, and more complex AST-based tools, which are more powerful but harder to use and significantly slower. Instead, Retrie uses equations expressed in Haskell syntax to describe the transformations you want to apply to your code. For example, the following equation:
forall f g xs. map f (map g xs) = map (f . g) xs
can be used to transform a function foo
from this:
module MyModule where
foo :: [Int] -> [Int]
foo ints = map bar (map baz ints)
to this:
module MyModule where
foo :: [Int] -> [Int]
foo ints = map (bar . baz) ints
In case you would like to push back this change, you could easily do this using this equation:
forall f g xs. map (f . g) xs = map f (map g xs)
The above equations could be applied to all files in a directory using the following command line syntax:
retrie --adhoc "forall f g xs. map f (map g xs) = map (f . g) xs"
Retrie can rewrite expressions, types, and patterns, and uses GHC's parser for full syntax support. On the plus side, Retrie respects local scoping, so it will not introduce shadowing/capture bugs and does not allow to rewrite incomplete expression fragments. Additionally, it automatically adds or remove parenthesis as needed.
As mentioned, Facebook used Retrie to safely migrate their Sigma anti-abuse engine’s rules to new APIs and libraries.
As a final note, you can also integrate Retrie as a library and directly use its monadic DSL in an Haskell program to enable more advanced refactoring.