Qualified imports

Alastair David Reid reid@cs.utah.edu
16 Aug 2001 12:43:38 -0600


Mark Carroll <mark@chaos.x-philes.com> writes:
> In writing a large program, do people tend to just name all their
> functions differently, or do they make much use of qualified imports?

In my experience, qualified names are mostly used to eliminate
conflicts rather than being used consistently on all symbols and
people tend to avoid introducing conflicts within their code and
between their code and the standard libraries.

Some reasons for this include:

1) If you insist on qualifying all Prelude symbols, you get code like:

     1 Prelude.+ 2 Prelude.* 3

   foo n | n > 0             = ...  -- should qualify > and 0 too...
         | Prelude.otherwise = ...

2) It's quite common to bundle a bunch of related code together:

   module Graphics( module Window, module Fonts, module Events, ... ) where
   import Window
   import Fonts
   import Events
   ...

   You can't do this if names in Window and Fonts (say) overlap because
   they would have to be referred to by the same prefix.  

   An early version of my graphics library ran into this problem - I
   tried to use the names "create", "delete" and "select" for
   creation, etc. of fonts, drawing styles, colours, etc. and got
   a whole bunch of complaints from users.

But some counterexamples exist.  For example, I share names between
modules like PrettyPrint and Parse which do related but different
things.  For example, I have:

  Pretty.int :: Int -> Doc   -- print an int e.g., int 3 -> "3"
  Parse.int  :: Parse Int    -- parse an int

  Pretty.quote :: Doc -> Doc          -- print argument adding quotes round it
                                      -- e.g., quote (int 3) -> "'3'"
  Parse.quote  :: Parse a -> Parse a  -- parse something with quotes round it
                                      -- e.g., (quote int) "'3'" -> 3

This emphasises the parallels between these types, makes it easier to
remember the names of the functions and avoids me having to think of
new names for them.  In practice, very few modules import both Pretty
and Parse - the ones most likely to import both seem to be Main
modules.

Or in a compiler I might have different sets of datatypes for code
that has been parsed but not typechecked or desugared and for code
that have been typechecked and desugared.  Both sets of datatypes need
things like expressions, declarations, etc. so it's natural to have:

  Abstract.Expr   -- expressions in "abstract syntax" (i.e., fresh from parser)
  Core.Expr       -- expressions in "core syntax" (after desugaring)

Again, very few modules import both types.  The only one in my
compiler is the module which converts between these two datatypes and
there it helps clarify the structure of the system to add qualifiers.
(Incidentally, I import these modules using "import qualified Abstract
as A" and "import qualified Core as C" to avoid being overwhelmed by
clutter.)

In both cases I could avoid this overlap but because I still want to
emphasise relationships, I'd do that by adding a prefix so I might as
well use qualified names.  With these examples, I don't run into the
bundling problem above because they tend not to be names I want to
bundle together.

Hope this helps,

-- 
Alastair Reid        reid@cs.utah.edu        http://www.cs.utah.edu/~reid/