.. _monad-comprehensions:
Monad comprehensions
--------------------
.. index::
single: monad comprehensions
.. extension:: MonadComprehensions
:shortdesc: Enable monad comprehensions.
:since: 7.2.1
Enable list comprehension syntax for arbitrary monads.
Monad comprehensions generalise the list comprehension notation,
including parallel comprehensions (:ref:`parallel-list-comprehensions`)
and transform comprehensions (:ref:`generalised-list-comprehensions`) to
work for any monad.
Monad comprehensions support:
- Bindings: ::
[ x + y | x <- Just 1, y <- Just 2 ]
Bindings are translated with the ``(>>=)`` and ``return`` functions
to the usual do-notation: ::
do x <- Just 1
y <- Just 2
return (x+y)
- Guards: ::
[ x | x <- [1..10], x <= 5 ]
Guards are translated with the ``guard`` function, which requires a
``MonadPlus`` instance: ::
do x <- [1..10]
guard (x <= 5)
return x
- Transform statements (as with :extension:`TransformListComp`): ::
[ x+y | x <- [1..10], y <- [1..x], then take 2 ]
This translates to: ::
do (x,y) <- take 2 (do x <- [1..10]
y <- [1..x]
return (x,y))
return (x+y)
- Group statements (as with :extension:`TransformListComp`):
::
[ x | x <- [1,1,2,2,3], then group by x using GHC.Exts.groupWith ]
[ x | x <- [1,1,2,2,3], then group using myGroup ]
- Parallel statements (as with :extension:`ParallelListComp`):
::
[ (x+y) | x <- [1..10]
| y <- [11..20]
]
Parallel statements are translated using the ``mzip`` function, which
requires a ``MonadZip`` instance defined in
:base-ref:`Control.Monad.Zip.`:
::
do (x,y) <- mzip (do x <- [1..10]
return x)
(do y <- [11..20]
return y)
return (x+y)
All these features are enabled by default if the :extension:`MonadComprehensions`
extension is enabled. The types and more detailed examples on how to use
comprehensions are explained in the previous chapters
:ref:`generalised-list-comprehensions` and
:ref:`parallel-list-comprehensions`. In general you just have to replace
the type ``[a]`` with the type ``Monad m => m a`` for monad
comprehensions.
.. note::
Even though most of these examples are using the list monad, monad
comprehensions work for any monad. The ``base`` package offers all
necessary instances for lists, which make :extension:`MonadComprehensions`
backward compatible to built-in, transform and parallel list
comprehensions.
More formally, the desugaring is as follows. We write ``D[ e | Q]`` to
mean the desugaring of the monad comprehension ``[ e | Q]``:
.. code-block:: none
Expressions: e
Declarations: d
Lists of qualifiers: Q,R,S
-- Basic forms
D[ e | ] = return e
D[ e | p <- e, Q ] = e >>= \p -> D[ e | Q ]
D[ e | e, Q ] = guard e >> \p -> D[ e | Q ]
D[ e | let d, Q ] = let d in D[ e | Q ]
-- Parallel comprehensions (iterate for multiple parallel branches)
D[ e | (Q | R), S ] = mzip D[ Qv | Q ] D[ Rv | R ] >>= \(Qv,Rv) -> D[ e | S ]
-- Transform comprehensions
D[ e | Q then f, R ] = f D[ Qv | Q ] >>= \Qv -> D[ e | R ]
D[ e | Q then f by b, R ] = f (\Qv -> b) D[ Qv | Q ] >>= \Qv -> D[ e | R ]
D[ e | Q then group using f, R ] = f D[ Qv | Q ] >>= \ys ->
case (fmap selQv1 ys, ..., fmap selQvn ys) of
Qv -> D[ e | R ]
D[ e | Q then group by b using f, R ] = f (\Qv -> b) D[ Qv | Q ] >>= \ys ->
case (fmap selQv1 ys, ..., fmap selQvn ys) of
Qv -> D[ e | R ]
where Qv is the tuple of variables bound by Q (and used subsequently)
selQvi is a selector mapping Qv to the ith component of Qv
Operator Standard binding Expected type
--------------------------------------------------------------------
return GHC.Base t1 -> m t2
(>>=) GHC.Base m1 t1 -> (t2 -> m2 t3) -> m3 t3
(>>) GHC.Base m1 t1 -> m2 t2 -> m3 t3
guard Control.Monad t1 -> m t2
fmap GHC.Base forall a b. (a->b) -> n a -> n b
mzip Control.Monad.Zip forall a b. m a -> m b -> m (a,b)
The comprehension should typecheck when its desugaring would typecheck,
except that (as discussed in :ref:`generalised-list-comprehensions`) in the
"then ``f``" and "then group using ``f``" clauses, when the "by ``b``" qualifier
is omitted, argument ``f`` should have a polymorphic type. In particular, "then
``Data.List.sort``" and "then group using ``Data.List.group``" are
insufficiently polymorphic.
Monad comprehensions support rebindable syntax
(:ref:`rebindable-syntax`). Without rebindable syntax, the operators
from the "standard binding" module are used; with rebindable syntax, the
operators are looked up in the current lexical scope. For example,
parallel comprehensions will be typechecked and desugared using whatever
"``mzip``" is in scope.
The rebindable operators must have the "Expected type" given in the
table above. These types are surprisingly general. For example, you can
use a bind operator with the type
::
(>>=) :: T x y a -> (a -> T y z b) -> T x z b
In the case of transform comprehensions, notice that the groups are
parameterised over some arbitrary type ``n`` (provided it has an
``fmap``, as well as the comprehension being over an arbitrary monad.