Module Import and Export strategies


For the most part, I have really enjoyed working with Haskell’s import system. It’s also an area I’ve run into confusing issues when the scenario is a bit more advanced than the simple imports we all normally do most of the time. In particular, I get a bit confused when trying to make an “Import” module that contains a bunch of re-exports for stuff I’m going use over and over in a bunch of other modules, or when normally using qualified, needing to drop that in certain scenarios.

For example, maybe the library is a client for some API, and you group functions into modules according to the upstream API, and then each of those modules has the same 20-lines of imports that you need for the API functions. Eventually, it seems better to consolidate the imports into a module that exports those functions/data types / modules you want to use (and then in each module there is a single import Import rather than the 20). and are examples I could easily locate, though I have seen a few variants on this in the past.

A few questions:

  • Do you know of any useful resources on this topic you would recommend?
  • Or maybe you can help me scope out some of the best practices here?
  • Out of the resources I’ve listed below are any of them grossly out of date?
  • Is there a reasonable way to export multiple modules (such as Text and ByteString, given their conflicts over concat/etc) from an Import module?.

Some existing prior art, specifically on modules/imports:

EDIT: Text/ByteString (both lazy/regular) in particular (and vector sometimes as well) often push the experience over the edge from reasonable to unreasonable… any recommendations for wrangling the conflicts that result from using those libs together in practice? I generally succumb to hacks (such as keeping those out of my import module, and having a smaller group of qualified imports for the T/BL/BS group).

1 Like

There is a proposal to make it possible to export modules qualified (among other things): They give this example:

module MyPrelude ( module qualified BL
                 , module qualified BS
                 , Set
                 , module qualified Set ) where

import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString as BS
import Data.Set ( Set )
import qualified Data.Set as Set

I think it has a good change of success, but there is no concrete implementation plan yet. So I don’t think this will land in GHC before 9.6 or something.

1 Like

Those resources don’t mention how to import pattern synonyms. You need to do something like

{-# LANGUAGE PatternSynonyms #-}
import Data.Sequence (pattern (:<|))

if the pattern is not “bundled” with the data type.

1 Like

Thank you for reviewing that, and sharing your insights.
I’ll add that to my reading list as well :slight_smile:

Nice! Implementing that sounds like a fun and useful project. I wouldn’t know where to begin, but would be happy to be mentored and help others make it happen.

I do wonder if the current improvements to vector/text/bytestring-land under discussion might end up also reducing the occurrence of these conflicts as a side-effect (or reducing the need for complicated qualified import/exports). That would be nice.