.. _recursive-do-notation:
The recursive do-notation
-------------------------
.. extension:: RecursiveDo
:shortdesc: Enable recursive do (mdo) notation.
:since: 6.8.1
Allow the use of recursive ``do`` notation.
The do-notation of Haskell 98 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 such recursive bindings do indeed make sense for a
variety of monads, but not all. In particular, recursion in this sense
requires a fixed-point operator for the underlying monad, captured by
the ``mfix`` method of the ``MonadFix`` class, defined in
``Control.Monad.Fix`` as follows: ::
class Monad m => MonadFix m where
mfix :: (a -> m a) -> m a
Haskell's ``Maybe``, ``[]`` (list), ``ST`` (both strict and lazy
versions), ``IO``, and many other monads have ``MonadFix`` instances. On
the negative side, the continuation monad, with the signature
``(a -> r) -> r``, does not.
For monads that do belong to the ``MonadFix`` class, GHC provides an
extended version of the do-notation that allows recursive bindings. The
:extension:`RecursiveDo` (language pragma: ``RecursiveDo``) provides the
necessary syntactic support, introducing the keywords ``mdo`` and
``rec`` for higher and lower levels of the notation respectively. Unlike
bindings in a ``do`` expression, those introduced by ``mdo`` and ``rec``
are recursively defined, much like in an ordinary let-expression. Due to
the new keyword ``mdo``, we also call this notation the *mdo-notation*.
Here is a simple (albeit contrived) example:
::
{-# LANGUAGE RecursiveDo #-}
justOnes = mdo { xs <- Just (1:xs)
; return (map negate xs) }
or equivalently
::
{-# LANGUAGE RecursiveDo #-}
justOnes = do { rec { xs <- Just (1:xs) }
; return (map negate xs) }
As you can guess ``justOnes`` will evaluate to ``Just [-1,-1,-1,...``.
GHC's implementation the mdo-notation closely follows the original
translation as described in the paper `A recursive do for
Haskell `__, which
in turn is based on the work `Value Recursion in Monadic
Computations `__.
Furthermore, GHC extends the syntax described in the former paper with a
lower level syntax flagged by the ``rec`` keyword, as we describe next.
Recursive binding groups
~~~~~~~~~~~~~~~~~~~~~~~~
The extension :extension:`RecursiveDo` also introduces a new keyword ``rec``, which
wraps a mutually-recursive group of monadic statements inside a ``do``
expression, producing a single statement. Similar to a ``let`` statement
inside a ``do``, variables bound in the ``rec`` are visible throughout
the ``rec`` group, and below it. For example, compare
::
do { a <- getChar do { a <- getChar
; let { r1 = f a r2 ; rec { r1 <- f a r2
; ; r2 = g r1 } ; ; r2 <- g r1 }
; return (r1 ++ r2) } ; return (r1 ++ r2) }
In both cases, ``r1`` and ``r2`` are available both throughout the
``let`` or ``rec`` block, and in the statements that follow it. The
difference is that ``let`` is non-monadic, while ``rec`` is monadic. (In
Haskell ``let`` is really ``letrec``, of course.)
The semantics of ``rec`` is fairly straightforward. Whenever GHC finds a
``rec`` group, it will compute its set of bound variables, and will
introduce an appropriate call to the underlying monadic value-recursion
operator ``mfix``, belonging to the ``MonadFix`` class. Here is an
example:
::
rec { b <- f a c ===> (b,c) <- mfix (\ ~(b,c) -> do { b <- f a c
; c <- f b a } ; c <- f b a
; return (b,c) })
As usual, the meta-variables ``b``, ``c`` etc., can be arbitrary
patterns. In general, the statement ``rec ss`` is desugared to the
statement
::
vs <- mfix (\ ~vs -> do { ss; return vs })
where ``vs`` is a tuple of the variables bound by ``ss``.
Note in particular that the translation for a ``rec`` block only
involves wrapping a call to ``mfix``: it performs no other analysis on
the bindings. The latter is the task for the ``mdo`` notation, which is
described next.
The ``mdo`` notation
~~~~~~~~~~~~~~~~~~~~
A ``rec``-block tells the compiler where precisely the recursive knot
should be tied. It turns out that the placement of the recursive knots
can be rather delicate: in particular, we would like the knots to be
wrapped around as minimal groups as possible. This process is known as
*segmentation*, and is described in detail in Section 3.2 of `A
recursive do for
Haskell `__.
Segmentation improves polymorphism and reduces the size of the recursive
knot. Most importantly, it avoids unnecessary interference caused by a
fundamental issue with the so-called *right-shrinking* axiom for monadic
recursion. In brief, most monads of interest (IO, strict state, etc.) do
*not* have recursion operators that satisfy this axiom, and thus not
performing segmentation can cause unnecessary interference, changing the
termination behavior of the resulting translation. (Details can be found
in Sections 3.1 and 7.2.2 of `Value Recursion in Monadic
Computations `__.)
The ``mdo`` notation removes the burden of placing explicit ``rec``
blocks in the code. Unlike an ordinary ``do`` expression, in which
variables bound by statements are only in scope for later statements,
variables bound in an ``mdo`` expression are in scope for all statements
of the expression. The compiler then automatically identifies minimal
mutually recursively dependent segments of statements, treating them as
if the user had wrapped a ``rec`` qualifier around them.
The definition is syntactic:
- A generator ⟨g⟩ *depends* on a textually following generator ⟨g'⟩, if
- ⟨g'⟩ defines a variable that is used by ⟨g⟩, or
- ⟨g'⟩ textually appears between ⟨g⟩ and ⟨g''⟩, where ⟨g⟩ depends on
⟨g''⟩.
- A *segment* of a given ``mdo``-expression is a minimal sequence of
generators such that no generator of the sequence depends on an
outside generator. As a special case, although it is not a generator,
the final expression in an ``mdo``-expression is considered to form a
segment by itself.
Segments in this sense are related to *strongly-connected components*
analysis, with the exception that bindings in a segment cannot be
reordered and must be contiguous.
Here is an example ``mdo``-expression, and its translation to ``rec``
blocks:
::
mdo { a <- getChar ===> do { a <- getChar
; b <- f a c ; rec { b <- f a c
; c <- f b a ; ; c <- f b a }
; z <- h a b ; z <- h a b
; d <- g d e ; rec { d <- g d e
; e <- g a z ; ; e <- g a z }
; putChar c } ; putChar c }
Note that a given ``mdo`` expression can cause the creation of multiple
``rec`` blocks. If there are no recursive dependencies, ``mdo`` will
introduce no ``rec`` blocks. In this latter case an ``mdo`` expression
is precisely the same as a ``do`` expression, as one would expect.
In summary, given an ``mdo`` expression, GHC first performs
segmentation, introducing ``rec`` blocks to wrap over minimal recursive
groups. Then, each resulting ``rec`` is desugared, using a call to
``Control.Monad.Fix.mfix`` as described in the previous section. The
original ``mdo``-expression typechecks exactly when the desugared
version would do so.
Here are some other important points in using the recursive-do notation:
- It is enabled with the extension :extension:`RecursiveDo`, or the
``LANGUAGE RecursiveDo`` pragma. (The same extension enables both
``mdo``-notation, and the use of ``rec`` blocks inside ``do``
expressions.)
- ``rec`` blocks can also be used inside ``mdo``-expressions, which
will be treated as a single statement. However, it is good style to
either use ``mdo`` or ``rec`` blocks in a single expression.
- If recursive bindings are required for a monad, then that monad must
be declared an instance of the ``MonadFix`` class.
- The following instances of ``MonadFix`` are automatically provided:
List, Maybe, IO. Furthermore, the ``Control.Monad.ST`` and
``Control.Monad.ST.Lazy`` modules provide the instances of the
``MonadFix`` class for Haskell's internal state monad (strict and
lazy, respectively).
- Like ``let`` and ``where`` bindings, name shadowing is not allowed
within an ``mdo``-expression or a ``rec``-block; that is, all the
names bound in a single ``rec`` must be distinct. (GHC will complain
if this is not the case.)