Proposal: add new function "check" to Control.Monad

Jon Fairbairn jon.fairbairn at cl.cam.ac.uk
Wed Aug 26 13:52:59 EDT 2009


I've been having network problems, and in addition, I usually
use gmane to access this list, but Sebastian and Yitzchak's
messages never turned up there.

On 2009-08-24 at 09:59+0200 Sebastian Fischer wrote:
> > Add check function to Control.Monad
> >
> >    check :: (MonadPlus m) => (a -> Bool) -> a -> m a
> >    check p a
> >        | p a = return a
> >        | otherwise = mzero
> 
> I agree that such a function is occasionally useful. But I
> doubt your rationale:

> > Rationale: [...]
> >
> > but check is clearly more generally useful than guard. (guard = flip
> > (check . const) ())
> 
> This is a bit like saying that concatMap is more generally
> useful than map and concat because:

I think I wasn't clear. When I said "clearly" I thought that the
general usefulness was clear; the remark about guard was an
"and" rather than a "because". What makes it clear to me is the
types: check is more polymorphic in the sense that it has a type
variable where guard has ().

>     map f = concatMap ((:[]).f)
>     concat = concatMap id
> 
> Although this is correct, map and concat are smaller pieces
> that can be easily combined to concatMap:

>     concatMap f = concat . map f
> 
> So the question is whether check is useful enough to be
> included as a shortcut for a combination of simpler
> primitives (as was decided for  concatMap).

Well, while I agree with the general principle, (and have argued
for it strongly in the past), I don't agree with the way you are
using "simple" here. When designing a library, one should choose
the primitives so that future definitions are simple, both in
the complexity of the terms needed to define them and in the
thought needed to define them, rather than the simplicity of
definition of the primitives themselves. The reason that I
suggested check is that it seems to me that it gives both of
those things in a way that guard doesn't (and people may have to
work a bit when weighing this, owing to the long-standing
familiarity of guard). The reason I didn't suggest removal of
guard was that long-standing familiarity.

To my mind, MonadPlus m => m () is a very specific and rather
peculiar type (consider [()], for example) and I think guard
suggests a rather imperative outlook because of this. Getting
from m () to Maybe Char strikes me as a longer thought process
than getting from m a to Maybe Char.

> > so we would expect a function
> >
> >   mfilter = (join .) . liftM . check
> >
> > to be useful.  Should that also be added?
> 
> I would not object and prefer this definition:
> 
>     mfilter f m = m >>= check f
> 
> It seems simpler.

I agree.  The equation above was just the derivation.

 Jón

-- 
Jón Fairbairn                              Jon.Fairbairn at cl.cam.ac.uk


More information about the Libraries mailing list