Skip to content

RFC: Automated formatting of the codebase #8936

Closed
@Kleidukos

Description

@Kleidukos

During this week's cabal maintainers call, the subject of contributors churn was raised, and an example that I noticed in some PRs was indentation. This is of course not a clear cut subject, because nobody wants to read weirdly-indented code but also we hate to manually indent stuff (especially on large patches), and personally I groan each time I get an entirely justified comment related to formatting.

I'd like to submit to the audience that we adopt a single formatting style of the codebase.

The tool is Fourmolu, a fork of Ormolu which integrates configuration flags, as opposed to the One True Style promoted by Ormolu.
Formolu comes with a GitHub Action, an interactive configuration visualiser.

One main reason that I'm suggesting Fourmolu is that we own the style. One very real criticism of late introduction of a formatter in a codebase is that we end up with a big commit that is just "format lol". Ormolu itself promotes a single style but this style evolves, and Fourmolu gives us the ability to disable the new additions to Ormolu. For me personally I would say it would remove quite a lot of frustration because I only want 1 reformatting commit and that's it.

In practice we can end up with conflicts between Ormolu and HLint: #1003 (Parentheses between some single class constraints conflict with hlint rules), which is why I'm in favour of actively owning our style, yet be able to automatically enforce it rather than spend time and energy doing it manually.

Moreover, Fourmolu options reduce diffs, which is a net benefit when reviewing PRs.

Here what it could look like:

diff --git a/Lol.hs b/Lol.hs
index 83772f1..dec18b5 100644
--- a/Lol.hs
+++ b/Lol.hs
@@ -1,16 +1,25 @@
-build    :: PackageDescription  -- ^ Mostly information from the .cabal file
-         -> LocalBuildInfo      -- ^ Configuration information
-         -> BuildFlags          -- ^ Flags that the user passed to build
-         -> [ PPSuffixHandler ] -- ^ preprocessors to run before compiling
-         -> IO ()
+build
+  :: PackageDescription
+  -- ^ Mostly information from the .cabal file
+  -> LocalBuildInfo
+  -- ^ Configuration information
+  -> BuildFlags
+  -- ^ Flags that the user passed to build
+  -> [PPSuffixHandler]
+  -- ^ preprocessors to run before compiling
+  -> IO ()
 build pkg_descr lbi flags suffixes = do
   checkBuildProblems verbosity (compiler lbi) flags
   targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags)
   let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets)
-  info verbosity $ "Component build order: "
-                ++ intercalate ", "
-                    (map (showComponentName . componentLocalName . targetCLBI)
-                        componentsToBuild)
+  info verbosity $
+    "Component build order: "
+      ++ intercalate
+        ", "
+        ( map
+            (showComponentName . componentLocalName . targetCLBI)
+            componentsToBuild
+        )
   when (null targets) $
     -- Only bother with this message if we're building the whole package
     setupMessage verbosity "Building" (packageId pkg_descr)
@@ -24,26 +33,28 @@ build pkg_descr lbi flags suffixes = do
     let comp = targetComponent target
         clbi = targetCLBI target
     componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity
-    let bi     = componentBuildInfo comp
+    let bi = componentBuildInfo comp
         progs' = addInternalBuildTools pkg_descr lbi bi (withPrograms lbi)
-        lbi'   = lbi {
-                   withPrograms  = progs',
-                   withPackageDB = withPackageDB lbi ++ [internalPackageDB],
-                   installedPkgs = index
-                 }
+        lbi' =
+          lbi
+            { withPrograms = progs'
+            , withPackageDB = withPackageDB lbi ++ [internalPackageDB]
+            , installedPkgs = index
+            }
     mb_ipi <- buildComponent verbosity (buildNumJobs flags) pkg_descr
-    par_strat <- toFlag <$> case buildUseSemaphore flags of
-                  Flag sem_name -> case buildNumJobs flags of
-                                    Flag {} -> do
-                                      warn verbosity $ "Ignoring -j due to -semaphore"
-                                      return $ UseSem sem_name
-                                    NoFlag -> return $ UseSem sem_name
-                  NoFlag -> return $ case buildNumJobs flags of
-                              Flag n -> NumJobs n
-                              NoFlag -> Serial 
+    par_strat <-
+      toFlag <$> case buildUseSemaphore flags of
+        Flag sem_name -> case buildNumJobs flags of
+          Flag{} -> do
+            warn verbosity $ "Ignoring -j due to -semaphore"
+            return $ UseSem sem_name
+          NoFlag -> return $ UseSem sem_name
+        NoFlag -> return $ case buildNumJobs flags of
+          Flag n -> NumJobs n
+          NoFlag -> Serial
     return (maybe index (Index.insert `flip` index) mb_ipi)
-  
+
   return ()
- where
-  distPref  = fromFlag (buildDistPref flags)
-  verbosity = fromFlag (buildVerbosity flags)
+  where
+    distPref = fromFlag (buildDistPref flags)
+    verbosity = fromFlag (buildVerbosity flags)

Personally I believe that the combo of normalisation, automation and ownership of the coding style is something that could remove a certain burden from our contributors and reviewers.

PS: Of course I would provide the initial CI setup and reformatting PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type: RFCRequests for Comment

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions