wiki:MonadComprehensions

Monad comprehensions

After a long absence, monad comprehensions are back, thanks to George Giorgidze and his colleagues. With {-# LANGUAGE MonadComprehensions #-} the comprehension [ f x | x <- xs, x>4 ] is interpreted in an arbitrary monad, rather than being restricted to lists. Not only that, it also generalises nicely for parallel/zip and SQL-like comprehensions. The aforementioned generalisations can be turned on by enabling the MonadComprehensions extension in conjunction with the ParallelListComp and TransformListComp extensions.

Rebindable syntax is fully supported for standard monad comprehensions with generators and filters. We also plan to allow rebinding of the parallel/zip and SQL-like monad comprehension notations.

For further details and usage examples, see the paper "Bringing back monad comprehensions" http://db.inf.uni-tuebingen.de/files/giorgidze/haskell2011.pdf MonadComp at the 2011 Haskell Symposium.

See ticket #4370.

Translation rules

Variables    : x and y
Expressions  : e, f and g
Patterns     : w
Qualifiers   : p, q and r

The main translation rule for monad comprehensions.

[ e | q ] = [| q |] >>= (return . (\q_v -> e))

(.)_v rules. Note that _v is a postfix rule application.

(w <- e)_v = w
(let w = d)_v = w
(g)_v = ()
(p , q)_v = (p_v,q_v)
(p | v)_v = (p_v,q_v)
(q, then f)_v = q_v
(q, then f by e)_v = q_v
(q, then group by e using f)_v = q_v
(q, then group using f)_v = q_v

[|.|] rules.

[| w <- e |] = e
[| let w = d |] = return d
[| g |] = guard g
[| p, q |] = ([| p |] >>= (return . (\p_v ->  [| q |] >>= (return . (\q_v -> (p_v,q_v)))))) >>= id
[| p | q |] = mzip [| p |] [| q |]
[| q, then f |] = f [| q |]
[| q, then f by e |] = f (\q_v -> e) [| q |]
[| q, then group by e using f |] = (f (\q_v -> e) [| q |]) >>= (return . (unzip q_v))
[| q, then group using f |] = (f [| q |]) >>= (return . (unzip q_v))

unzip (.) rules. Note that unzip is a desugaring rule (i.e., not a function to be included in the generated code).

unzip () = id
unzip x  = id
unzip (w1,w2) = \e -> ((unzip w1) (e >>= (return .(\(x,y) -> x))), (unzip w2) (e >>= (return . (\(x,y) -> y))))

Examples

Some translation examples using the do notation to avoid things like pattern matching failures are:

[ x+y | x <- Just 1, y <- Just 2 ]

-- translates to:
do x <- Just 1
   y <- Just 2
   return (x+y)

Transform statements:

[ x | x <- [1..], then take 10 ]

-- translates to:
take 10 (do
  x <- [1..]
  return x)

Grouping statements (note the change of types):

[ (x :: [Int]) | x <- [1,2,1,2], then group by x ] :: [[Int]]

-- translates to:
do x <- mgroupWith (\x -> x) [1,2,1,2]
   return x

Parallel statements:

[ x+y | x <- [1,2,3]
      | y <- [4,5,6] ]

-- translates to:
do (x,y) <- mzip [1,2,3] [4,5,6]
   return (x+y)

Note that the actual implementation is not using the do-Notation, it's only used here to give a basic overview about how the translation works.

Implementation details

Monad comprehensions had to change the StmtLR data type in the hsSyn/HsExpr.lhs file in order to be able to lookup and store all functions required to desugare monad comprehensions correctly (e.g. return, (>>=), guard etc). Renaming is done in rename/RnExpr.lhs and typechecking in typecheck/TcMatches.lhs. The main desugaring is done in deSugar/DsListComp.lhs. If you want to start hacking on monad comprehensions I'd look at those files first.

Some things you might want to be aware of:

[todo]

Last modified 2 years ago Last modified on Jan 4, 2012 8:50:09 AM