I am currently using the
NoImplicitPrelude extension and thus regularly add explicit imports like this:
import Data.Bool (Bool (True))
import Data.Maybe (Maybe (Just, Nothing))
import Data.Eq (Eq (=))
-- ... much, much more imports
While that might seem masochistic at first, the reason is the Haskell Language Server and how it helps my IDE to efficiently manage my imports for me. My workflow is like this:
- write a couple of lines of code without giving thought to imports
- using my emacs shortcut for “lsp-execute-code-action” on every compiler error (expression underlined in red)
- my IDE presents me with a list of choices, among those to add an import (e.g.
lsp-format-buffer to apply the brittany code formatter and have my imports look nice
- after having worked out all the errors there is an IDE command to “remove all redundant imports” and I’m done
All my imports are explicit (and I use qualified imports whereever it helps readibility, this is well supported by HLS, too). Of course I can have all-explicit imports with
Prelude, too. The above would simply looks like the follwing:
import Prelude (Bool (True), Maybe (Just, Nothing), Eq (=))
… but given how my IDE works, this is hardly more practical and arguably less readable.
There are advantages and disadvantages to my approach.
- Being 100% explicit (as described above)
- Routinely making an explicit choice what to import from where, e.g.
Control.Category rather than from
Witherable or from
Data.Foldable rather than
- There is a bit of an educational experience. You find
Data.Functor. You find
Control.Applicative, you find
Data.Traversable and so on. There are reasons behind these modules that can be interesting especially when still learning.
- Whenever I find myself adding those very basic imports, I wish for some sort of prelude that would take care of that—because even with my IDE this is repetitive work. (I then am reminded of the downsides of the existing
Prelude—partial functions, and simply a lot of stuff that I don’t use like
foldl—and I definitely don’t want to add my own, new, custom
Prelude unless I have a very good reason.
- The explicit imports like the ones above might confuse other programmers rather than help with readability: Am I hiding a custom reimplementation of
Maybe within those imports?
import Prelude hiding (Maybe) is much easier to understand
- When I split up code of one module into two modules, I am refactoring code that works. In order to get the imports right, I copy all the imports over to the new module and then run “remove all redundant imports”. Adding all imports anew even with the help of my IDE would be cumbersome.
- The import list gets even longer and takes up even more space on top of every module
Having read through this thread, I might give
relude a try. I was reluctant to use anything else than
Prelude because one of the added values of any sort of prelude would be that it’s fairly standard and makes my code faster to read rather than adding a dependency.