Automatically export Haskell modules

by Taylor Fausak on

I am happy to announce the 1.0.0 release of Autoexporter, a tool for automatically exporting Haskell modules.

What does that mean? Haskell modules can re-export other modules in addition to exporting things defined directly in them. In fact, my Haskell package checklist recommends providing a single module for users to import. But defining everything in one module could make it difficult to understand. So you will often see top-level modules like Data.Time that simply re-export modules below them in the hierarchy.

module Data.Time
  ( module Data.Time.Calendar
  , module Data.Time.Clock
  , module Data.Time.LocalTime
  , module Data.Time.Format
  ) where

import Data.Time.Calendar
import Data.Time.Clock
import Data.Time.LocalTime
import Data.Time.Format

Doing this by hand is tedious and error-prone. Every time you add a new module, you must remember to come back to this top-level module and re-export it. Also you have to spell it correctly not once but twice.

It is possible to make this a little better by using the so-called “import/export shortcut”. This aliases the imports so that you only have to import one thing. The Data.Time module could be cleaned up with this shortcut.

module Data.Time
  ( module Export
  ) where

import Data.Time.Calendar  as Export
import Data.Time.Clock     as Export
import Data.Time.LocalTime as Export
import Data.Time.Format    as Export

This solves the problem of having to write out each module name twice, but it introduces a new problem: The generated documentation is completely blank. This is a long-standing bug in Haddock, the Haskell documentation tool.

Now there is a better way! Autoexporter does this tedious work for you. Instead of manually listing the modules you want to export, add Autoexporter as a dependency. Then replace the entire module with this:

-- Data.Time
{-# OPTIONS_GHC -F -pgmF autoexporter #-}

That will generate code for you that looks like the first example. It will automatically include all direct children of the module. So if that’s in Data.Time, it will include Data.Time.Calendar but not Data.Time.Calendar.Julian.

I find this to be very convenient, more so for applications than libraries. But in both cases it reduces the friction of adding more modules to a Haskell package.