# Generalized monad comprehensions

This is a proposal to generalize MonadComprehensions (in conjunction with the `ParallelListComp`

and `TransformListComp`

extensions) from ticket #8914 as well as do-notation.

## Related proposals

## Motivation

A `Monad`

constraint is sometimes too powerful both with with `MonadComprehensions`

extension and using do-notation.

### Functor

Ideally the following functions could be desugared into `fmap`

with a `Functor`

constraint:

```
{-# LANGUAGE MonadComprehensions #-}
fmapM :: Functor f => (a -> b) -> f a -> f b
fmapM f xs = [f x | x <- xs ]
fmapDo :: Functor f => (a -> b) -> f a -> f b
fmapDo f xs = do
x <- xs
return (f x)
```

but their inferred signatures are `Monad m => (a -> b) -> m a -> m b`

.

### Applicative functor

The following are equivalent to, and could be safely replaced with, `liftA3`

:

```
threeM :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
threeM f a1 a2 a3 = [f x1 x2 x3 | x1 <- a1, x2 <- a2, x3 <- a3 ]
threeDo :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
threeDo f a1 a2 a3 = do
x1 <- a1
x2 <- a2
x3 <- a3
return (f x1 x2 x3)
```

but get a more restricted `Monad`

constraint.

### MonadZip

`MonadZip`

(from Control.Monad.Zip) is a type class that requires a `Monad`

constraint but could make do with a `Functor`

constraint (or possibly no constraint at all but then the default implementations could not be implemented and the laws could not be expressed. The definition is currently^{ source}

```
class Monad m => MonadZip m where
mzip :: m a -> m b -> m (a,b)
mzip = mzipWith (,)
mzipWith :: (a -> b -> c) -> m a -> m b -> m c
mzipWith f ma mb = liftM (uncurry f) (mzip ma mb)
munzip :: m (a,b) -> (m a, m b)
munzip mab = (liftM fst mab, liftM snd mab)
```

but could be

```
class Functor f => FunctorZip f where
fzip :: f a -> f b -> f (a,b)
fzip = fzipWith (,)
fzipWith :: (a -> b -> c) -> f a -> f b -> f c
fzipWith f fa fb = fmap (uncurry f) (fzip fa fb)
funzip :: f (a,b) -> (f a, f b)
funzip fab = (fmap fst fab, fmap snd fab)
```

I noticed this in particular when I wanted to use length-indexed lists with a monad comprehension using *only* the (trivially defined) `Applicative`

and `FunctorZip`

instances, but was forced to define the `Monad`

instance that was of no use for my particular case.

Note that `FunctorZip`

is `Applicative`

minus `pure`

. This is because te fundamental operations of both is combining/accumulating. Specifically, `Applicative`

accumulates a closure while`FunctorZip`

accumulates a tuple, but mapping lets you covert between the two after the fact, so it doesn't matter.

```
fzip fa fb = (,) <$> fa <*> fb
fzipWith f fa fb = f <$> fa <*> fb
```

vs

`f <*> fa = fzipWith ($) f fa`

## Specification

*To be done'' *