A software engineer website

Haskell refactorings

Gautier DI FOLCO February 11, 2024 [Software engineering] #software engineering #design #haskell

I was looking through Martin Fowler's refactorings list which is heavily OOP/Imperative-oriented.

There are currently 66 refactorings:

Note: responsibilities distribution is often involving encapsulation, but I have done my best to make a distinction between refactorings which are more agnostic and those working through OOP concepts.

If I try to summarize which refactorings I use, there are far less.

smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)

Becomes

mkSmoothie :: [Fruit] -> Smoothie
mkSmoothie = pour milk . runBlender . map (cut . peel)
smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)

Becomes

smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map prepareFruit
  where prepareFruit = cut . peel
smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map prepareFruit
  where prepareFruit = cut . peel

Becomes

smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)
smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)

Becomes

smoothie :: [Fruit] -> Smoothie
smoothie fruits = pour milk $ runBlender $ map (cut . peel) fruits
smoothie :: [Fruit] -> Smoothie
smoothie fruits = pour milk $ runBlender $ map (cut . peel) fruits

Becomes

smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)
smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)

Becomes

smoothie :: Liquid -> [Fruit] -> Smoothie
smoothie liquid = pour liquid . runBlender . map (cut . peel)

Alternatively, we can keep the function as a default implementation:

smoothie :: [Fruit] -> Smoothie
smoothie = smoothieWith liquid

smoothieWith :: Liquid -> [Fruit] -> Smoothie
smoothieWith liquid = pour liquid . runBlender . map (cut . peel)
smoothie :: [Fruit] -> Smoothie
smoothie = pour milk . runBlender . map (cut . peel)

Becomes

smoothie :: ([Slice] -> a) -> [Fruit] -> a
smoothie mix = mix . map (cut . peel)

That roughly 7 refactorings for local rearrangements, and these are probably 90%-95% of the refactorings I use daily (and I use them a lot).

Sure, there are few others at type-level but despite that, there are far less than in the OOP ecosystem.

Which does not seem intuitive to me as Haskell (at least GHC's version) is a quite large programming language compared to other OOP programming languages. I guess FP simpler core plays a role in that.

Back to top