Skip to content

Re-Exports

The final topic we need to touch on is that of re-exports. Let's stick with our example of implementing the Banner system in Haskell. We would probably have a list of modules that implement the different components of the system. Something like

Banner.StudentRecord
Banner.Transcript
Banner.Database
Banner.WebInterface

and probably a whole list of additional modules. Splitting the code base into separate modules that implement functions to manipulate student records, to manipulate transcripts, to talk to the database backend, and to talk to a web frontend allows us to organize our code into self-contained components with a well-defined interface between them. However, an application working with a StudentRecord often also wants to access the student's Transcript, and probably retrieves the records to work with from the Banner database. Thus, it needs functions from Banner.StudentRecord, Banner.Transcript, and Banner.Database. It needs to import all three modules

import Banner.StudentRecord
import Banner.Transcript
import Banner.Database

This gets tedious quickly. To this end, we would like to create a wrapper module Banner that provides all the most commonly used functions of the Banner system. To this end, the Banner module imports all the commonly used functions from the specialized modules and then simply re-exports them by adding them to its export list, like so:

Banner.hs
module Banner
    ( StudentRecord
    , bannerNumber
    , name
    , address
    , transcript
    , Transcript
    , retrieveRecords
    , storeRecords
    , processWebQuery
    ) where

import Banner.StudentRecord
    (StudentRecord, bannerNumber, name, address, transcript)
import Banner.Transcript
    (Transcript)
import Banner.Database
    (retrieveRecords, storeRecords)
import Banner.WebInterface
    (processWebQuery)

For most use cases, it will now be enough to simply import Banner and use the types and functions exported by Banner. Banner probably won't export every function exported by the more specialized modules. We would then import those specialized modules only when we need functions they provide that are not re-exported by Banner.

As a more concrete example, you may remember that the Haskell Prelude is the module automatically imported into every Haskell source code file and which provides a long list of standard library functions. The Prelude does not define any of those functions itself. It consists of a long list of imports from Data.List, Data.Maybe, Data.Either, etc, etc, etc, and then it re-exports those imports.

The list of functions imported from Data.List includes the most commonly used ones, such as map and filter, but it does not include more esoteric ones, such as unfoldr. Thus, when we only need map and filter, there is no need to import Data.List. If we need unfoldr, we have to import it explicitly from Data.List.