Haskell code in the wild could typically have type signatures formatted like this:
sortByM :: Monad m
=> (a -> a -> m Bool)
-> [a]
-> m [a]
From now on I will call this "silly formatting", to contrast with the proper way to split the same type signature to multiple lines:
sortByM ::
Monad m =>
-> a -> m Bool) ->
(a ->
[a] m [a]
With proper formatting the context and parameters take whole lines. We could easily tell which part is the context, which are parameters, and which is the function output.
Had we wanted to add or remove parameters or the context, with silly formatting we would need to make edits from the middle of one line to the middle of another. If you are interested in collaboration, silly formatting will hit you with spurious merge conflicts!
What would be the silly way to format function bodies? Here:
= pure []
sortByM _ [] :xs) = partitionM (predicate x) xs >>=
sortByM predicate (x-> sortByM predicate pre <> ((x :) <$> sortByM predicate post) \(post, pre)
Note that to read this snippet you may have needed scroll horizontally - silly, isn't it?
Let's imagine that we wanted to rename predicate
to
pred
or to p
. With silly formatting we would
also need to modify all of the lines in the function body! That's more
work and yet again more work due to unnecessary merge conflicts!
But there's more: silly formatting is difficult to read! Subjectively it's much easier to get used to scanning the text aligned to the left, rather than different silly alignment every time, often far to the right side of the screen or beyond!
If we sacrifice the fancy custom alignments and just indent blocks with simple and consistent 4 spaces, it may cost us an extra line of code but the benefit in easy maintainability and readabilty is worth it in my opinion:
= pure []
sortByM _ [] :xs) =
sortByM predicate (x>>=
partitionM (predicate x) xs -> sortByM predicate pre <> ((x :) <$> sortByM predicate post) \(post, pre)
What drove me to write this post
The Haskell experience has recently gotten much better with haskell-language-server and its VS Code integration.
The remaining aspect where my Haskell experience was lacking in
comparison to C++ was auto-formatting, where I enjoy letting
clang-format
auto-format my code. I was looking to see what
formatting tools exist for Haskell, and discovered that while several
tools exist, none fit my taste, and some of these tools were
disqualified on the grounds of applying silly formatting.
Among the differenct formatting tools, I'd like to praise ormolu
(and
fourmolu
) for applying more sensible formatting than
others.
Notes
- GHC's source code has a mixture of styles, with higher prevalence for silly style.
- The short type signature of
sortByM
may typically not be split over several lines. I used it as a simple example to keep the post short. - Discussions: r/haskell mostly thought that this post is the one being silly, and in Twitter the sentiment was more positive :)
- Image by LillyCantabile from Pixabay
Appendix
An alternative implementation of sortByM
sortByM :: Monad m => (a -> a -> m Bool) -> [a] -> m [a]
=
sortByM p case
\-> pure []
[] :xs) ->
(x
partitionM (p x) xs>>= both (sortByM p)
<&> \(post, pre) -> pre <> (x : post)
\case
lets us avoid a repetition of the name
sortByM
. Had we wanted to rename it, we'd touch less lines
of code. Personally I see this as a benefit!