Chapter 6. Language extensions supported by Hugs and GHC

These experimental features are enabled with the -98 option. Most are described in Section 7 of the Hugs 98 User Manual. Those described in this chapter are also supported by GHC with appropriate options, though in some cases the GHC versions are more general.

6.1. Syntactic extensions

6.1.1. Recursive do-notation

The recursive do-notation (also known as mdo-notation) is implemented as described in: A recursive do for Haskell, Levent Erkök and John Launchbury, Haskell Workshop 2002, pages: 29–37. Pittsburgh, Pennsylvania.

The do-notation of Haskell does not allow recursive bindings, that is, the variables bound in a do-expression are visible only in the textually following code block. Compare this to a let-expression, where bound variables are visible in the entire binding group. It turns out that several applications can benefit from recursive bindings in the do-notation, and this extension provides the necessary syntactic support.

Here is a simple (yet contrived) example:

import Control.Monad.Fix

justOnes = mdo xs <- Just (1:xs)
               return xs
As you can guess justOnes will evaluate to Just [1,1,1,...

The Control.Monad.Fix module introduces the MonadFix class, defined as

class Monad m => MonadFix m where
    mfix :: (a -> m a) -> m a
The function mfix dictates how the required recursion operation should be performed. If recursive bindings are required for a monad, then that monad must be declared an instance of the MonadFix class. For details, see the above mentioned reference.

The Control.Monad.Fix module also defines instances of MonadFix for lists, Maybe and IO. Furthermore, several other monad modules provide instances of the MonadFix class, including the Control.Monad.ST and Control.Monad.ST.Lazy modules for Haskell's internal state monad (strict and lazy, respectively).

There are three important points in using the recursive-do notation:

  • The recursive version of the do-notation uses the keyword mdo (rather than do).

  • You should "import Control.Monad.Fix".

  • Hugs should be started with the flag -98.

The web page: "http://www.cse.ogi.edu/PacSoft/projects/rmb" contains up to date information on recursive monadic bindings.

Historical note: The old implementation of the mdo-notation (and most of the existing documents) used the name MonadRec for the class and the corresponding library.

6.1.2. Parallel list comprehensions (a.k.a. zip-comprehensions)

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. Given a parallel comprehension of the form:

[ e | p1 <- e11, p2 <- e12, ...
    | q1 <- e21, q2 <- e22, ...
    ...
]
This will be translated to:
[ e | ((p1,p2), (q1,q2), ...) <- zipN [(p1,p2) | p1 <- e11, p2 <- e12, ...]
                                      [(q1,q2) | q1 <- e21, q2 <- e22, ...]
                                      ...
]
where "zipN" is the appropriate zip for the given number of branches. These functions must be in scope; the Prelude defines zip and zip3, but if you want to handle 4 or more lists in parallel, you will need to import List or Data.List.