Object-oriented developers have a shared vocabulary for reshaping code without changing what it does — Fowler’s catalogue, Opdyke’s thesis, the Refactoring menu in every IDE — as well as a history of doing so informally before then. This is the same idea seen through the functional-programming imagination: each move takes a program from one structure to an equivalent one, and in a referentially transparent setting equivalent is something you can state and check.
Every entry that has a page shows the refactoring in Scala 3 and in Haskell, in both directions (the move and its inverse), with the papers and study material behind it, a diagram of the structural change, and a property-based test — Hedgehog in Scala and Hedgehog in Haskell — that exercises the before and after on generated inputs and demands the same answer. Entries without a link are queued; they will be filled in the order listed.
Refactorings
- Extract / Inline method
- Replace mutable fields with lenses
- Replace global state with parameter
- Introduce ReaderT (Kleisli)
- Replace loop with fold
- Replace recursion with fold
- Replace inheritance with natural transformation
- Replace exception with extended response type
- Replace null with optional
- Replace optional fields with sum types
- Replace sum types with optional fields
- Replace parameter with reduced type
- Replace mutable variable with recursion
- Replace similar methods with generic cousin
- Extract constant
- Merge into module
- Extract generic algebra
- Wrap side effect
- Pull out effect
- Lazy lists to eager arrows
- Replace unit tests with property-based test
With higher-kinded types
- Generalize over input constraint
- Isolate business key via data parametrization
- Remove recursion from GADT
- Replace visitor with type class
- Replace concrete classes with final tagless interpretation
- Replace final tagless with free monad
- Replace free monad with final tagless encoding
- Replace strategy with tagless final
- Replace subtypes with type class instances
- Replace pattern matching with type class dispatch
- Introduce higher-kinded constraint
With dependent types
- Introduce parameter-value-dependent constraint
- Introduce (path-)dependent type
- Replace (path-)dependent type with type class constraint