The hierarchical module system was originally proposed as an extension to the Haskell98 standard, and adopted formally in Haskell2010. It is typically regarded as one of the less controversial extensions, because more or less everyone agreed that single-token module names were liable to become a huge tangled mess with everyone stepping on each others' toes.
I lack a little historical context here, since the extension was widespread before I was introduced to Haskell, but I think that the current layout of the module hierarchy is unsatisfactory. Having been given hierarchical modules, Haskellers seem to feel obliged to use them: single-component names are virtually unheard of. Yet in many cases, the additional categorisation seems to add no semantic content whatsoever. What do we learn about a module by its name
Data.Bool that was not already evident in the
Bool? Why is the
Functor type class a piece of
Data but the closely-related
Applicative type class a
Control structure? Why do we have
There are certainly cases where the additional qualification adds meaning. Writing
import Haskell at the top of your file seems meaningless, where in
import Language.Haskell you have a slightly better idea of what is being requested. However, minimalism is desirable: when adding a component to your module name, ask yourself if it resolves any confusion or prevents any ambiguity. I would argue that in
Codec.Binary.UTF8.Generic, for example, nearly all of the name is redundant. There is no UTF-8 that is not a binary codec, and arguably the
Generic component of the name is equally unenlightening. Just name the module
UTF8, the shortest unambiguous description of its purpose.
One could argue that keeping module names long reduces the risk of collision. It's true that specifying more information in the module name might reduce the chance of some other module clashing with it, but often people confuse “information content” with “textual length”: clearly, grouping all monad-related modules under
Control.Monad instead of just
Monad is not going to stop two implementations of
Reader from interfering with each other. So keep just the meaningful component of the name: what, after all, could possibly be named
Monad except for a module housing the
Monad class and related utility functions? Likewise
IO: all sorts of concepts are clearly going to exist only once in Haskell. Those that don't are no better served being
If you really want to avoid name collisions, take a leaf from
syb's book: previously under the hierarchy
Data.Generics, which not only suffered from
Data-itis but also adequately described any generic programming mechanism,
syb is starting to move over to the new, more specific
Generics.SYB hierarchy. This drops the useless
Data prefix and instead uses a component – the name of the package – that is very likely to be unique to this particular design and implementation. We appear to lose some "generality", but in reality the knowledge that you were using SYB in particular was probably already encoded in your program, since other generics libraries will have made different design decisions. The new name also emphasises the position of
syb as a generics library, not the generics library – on an equal footing with Uniplate and other similar tools.
Hierarchical modules do make internal structuring of a project easier; one only needs to look at something like Haskore's module list to see that they could clearly not just all be dumped in a single source directory. So that is a legitimate use, but of course there's not necessarily any reason why the internal structure of your project has to be reflected in the external API you provide. If you want twenty helper modules in various tidy subdirectories, fine, but you can probably re-export everything relevant (and it is good design not to export too much) in just a few root modules at the base of your hierarchy. Don't confuse what makes life easy for the library author with what makes things easy for the library user – and don't assume you need to trade one off against the other.
In addition to the above practical concerns, I also somewhat object to the overuse of the poor little
. character. For example, one should in principle be able to write a list of all weekdays as
[Monday..], but this actually parses as a qualified reference to the
Monday module – you'll need to use the marginally uglier
[Monday ..]. This also demonstrates how the syntax for qualified operators is just plain ugly. It's hard to write and equally hard to read
7 Prelude.+ 8 or, to really rub it in,
f Control.Category.. g.
Hierarchical modules added some much-needed structure to Haskell's module namespace, but should be used sparingly and responsibly to avoid tragic keyboard wear every time I want to
import qualified Text.ParserCombinators.Parsec.Combinator as PCPC. The policy on how best to name your modules has historically been loose, and the coherence of the module landscape has suffered for it.