Version 8 (modified by simonpj, 5 years ago) (diff) |
---|

# 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 theMonadComprehensionsextension in conjunction with theParallelListCompandTransformListCompextensions.

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]