Rewriting to Haskell–Making GHC More Nitpicky

Posted on April 6, 2020 by Riccardo
Functional ProgrammingHaskellServant

This is part of a series:


GHC by default compiles with some warnings enabled. But there’s a ton more. Thus, we decided to turn on several. Also, we have the compiler treat them as errors with -Wall. Luckily, Max Tagher did all the work for us and prepared a “Copy-Pastable List” with nice explanations for us lazy developers. Thanks Max!

This is what we threw in package.yaml:

 library:
   source-dirs: src
+  ghc-options:
+    # For details on warnings: https://downloads.haskell.org/~ghc/master/users-guide/using-warnings.html
+    # This list taken from https://medium.com/mercury-bank/enable-all-the-warnings-a0517bc081c3
+    # Enable all warnings with -Weverything, then disable the ones we don’t care about
+    - -Weverything
+    - -Werror
+    - -Wno-missing-exported-signatures # missing-exported-signatures turns off the more strict -Wmissing-signatures. See https://ghc.haskell.org/trac/ghc/ticket/14794#ticket
+    - -Wno-missing-import-lists # Requires explicit imports of _every_ function (e.g. ‘$’); too strict
+    - -Wno-missed-specialisations # When GHC can’t specialize a polymorphic function. No big deal and requires fixing underlying libraries to solve.
+    - -Wno-all-missed-specialisations # See missed-specialisations
+    - -Wno-unsafe # Don’t use Safe Haskell warnings
+    - -Wno-safe # Don’t use Safe Haskell warnings
+    - -Wno-missing-local-signatures # Warning for polymorphic local bindings; nothing wrong with those.
+    - -Wno-monomorphism-restriction # Don’t warn if the monomorphism restriction is used
   exposed-modules:

Turns out we have been disciplined enough to just need a few edits to fix the new errors. What follows is a non-comprehensive list of the changes we performed together with the warning that was triggered.

-Wmissing-import-lists:

-module Api.Search where
+module Api.Search
+  ( SearchAPI,
+    getResults,
+  )
+where

-Wimplicit-prelude:

 import Servant ((:>), Get, Handler, JSON, QueryParam)
+import Prelude

-Wincomplete-patterns:

 instance FromJSON Configuration where
   parseJSON (Object x) = do
     -- ...
+  parseJSON _ = fail "was expecting an object"

-Wmissing-exported-signatures:

+joinComments :: Query
 joinComments = "LEFT JOIN comments ON posts.id = comments.post_id"

+textOrTitle :: Query
 textOrTitle = "(posts.text ILIKE ? OR posts.title ILIKE ?)"

Some warnings can be annoying or even unneeded depending on the use-case, your mileage may vary. Feel free to treat this as a starting point and tweak the options down the line.


Support my work by tweeting this article! 🙏