GHC supports a small extension to the syntax of module
names: a module name is allowed to contain a dot
‘.’. This is also known as the
“hierarchical module namespace” extension, because
it extends the normally flat Haskell module namespace into a
more flexible hierarchy of modules.

This extension has very little impact on the language
itself; modules names are always fully
qualified, so you can just think of the fully qualified module
name as "the module name". In particular, this
means that the full module name must be given after the
module keyword at the beginning of the
module; for example, the module A.B.C must
begin

module A.B.C

It is a common strategy to use the as
keyword to save some typing when using qualified names with
hierarchical modules. For example:

import qualified Control.Monad.ST.Strict as ST

Hierarchical modules have an impact on the way that GHC
searches for files. For a description, see Section 4.9.3.

GHC comes with a large collection of libraries arranged
hierarchically; see the accompanying library documentation.
There is an ongoing project to create and maintain a stable set
of "core" libraries used by several Haskell
compilers, and the libraries that GHC comes with represent the
current status of that project. For more details, see Haskell
Libraries.

The discussion that follows is an abbreviated version of Simon Peyton Jones's original proposal. (Note that the proposal was written before pattern guards were implemented, so refers to them as unimplemented.)

Suppose we have an abstract data type of finite maps, with a
lookup operation:

lookup :: FiniteMap -> Int -> Maybe Int

The lookup returns Nothing if the supplied key is not in the domain of the mapping, and (Just v) otherwise,
where v is the value that the key maps to. Now consider the following definition:

What is clunky doing? The guard ok1 &&
ok2 checks that both lookups succeed, using
maybeToBool to convert the Maybe
types to booleans. The (lazily evaluated) expectJust
calls extract the values from the results of the lookups, and binds the
returned values to val1 and val2
respectively. If either lookup fails, then clunky takes the
otherwise case and returns the sum of its arguments.

This is certainly legal Haskell, but it is a tremendously verbose and
un-obvious way to achieve the desired effect. Arguably, a more direct way
to write clunky would be to use case expressions:

This is a bit shorter, but hardly better. Of course, we can rewrite any set
of pattern-matching, guarded equations as case expressions; that is
precisely what the compiler does when compiling equations! The reason that
Haskell provides guarded equations is because they allow us to write down
the cases we want to consider, one at a time, independently of each other.
This structure is hidden in the case version. Two of the right-hand sides
are really the same (fail), and the whole expression
tends to become more and more indented.

The semantics should be clear enough. The qualifers are matched in order.
For a <- qualifier, which I call a pattern guard, the
right hand side is evaluated and matched against the pattern on the left.
If the match fails then the whole guard fails and the next equation is
tried. If it succeeds, then the appropriate binding takes place, and the
next qualifier is matched, in the augmented environment. Unlike list
comprehensions, however, the type of the expression to the right of the
<- is the same as the type of the pattern to its
left. The bindings introduced by pattern guards scope over all the
remaining guard qualifiers, and over the right hand side of the equation.

Just as with list comprehensions, boolean expressions can be freely mixed
with among the pattern guards. For example:

f x | [y] <- x
, y > 3
, Just z <- h y
= ...

Haskell's current guards therefore emerge as a special case, in which the
qualifier list has just one element, a boolean expression.

Parallel list comprehensions are a natural extension to list
comprehensions. List comprehensions can be thought of as a nice
syntax for writing maps and filters. Parallel comprehensions
extend this to include the zipWith family.

A parallel list comprehension has multiple independent
branches of qualifier lists, each separated by a `|' symbol. For
example, the following zips together two lists:

[ (x, y) | x <- xs | y <- ys ]

The behavior of parallel list comprehensions follows that of
zip, in that the resulting list will have the same length as the
shortest branch.

We can define parallel list comprehensions by translation to
regular comprehensions. Here's the basic idea:

GHC allows most kinds of built-in syntax to be rebound by
the user, to facilitate replacing the Prelude
with a home-grown version, for example.

You may want to define your own numeric class
hierarchy. It completely defeats that purpose if the
literal "1" means "Prelude.fromInteger
1", which is what the Haskell Report specifies.
So the -fno-implicit-prelude flag causes
the following pieces of built-in syntax to refer to
whatever is in scope, not the Prelude
versions:

Integer and fractional literals mean
"fromInteger 1" and
"fromRational 3.2", not the
Prelude-qualified versions; both in expressions and in
patterns.

Negation (e.g. "- (f x)")
means "negate (f x)" (not
Prelude.negate).

In an n+k pattern, the standard Prelude
Ord class is still used for comparison,
but the necessary subtraction uses whatever
"(-)" is in scope (not
"Prelude.(-)").

(The (...) part can be any context including the empty context; that part
is up to you.)
If the functions don't have the right type, very peculiar things may
happen. Use -dcore-lint to
typecheck the desugared program. If Core Lint is happy you should be all right.