-{-# OPTIONS_GHC -XNoImplicitPrelude #-}
+{-# LANGUAGE CPP, NoImplicitPrelude #-}
+
-----------------------------------------------------------------------------
-- |
-- Module : Control.Monad
, join -- :: (Monad m) => m (m a) -> m a
, msum -- :: (MonadPlus m) => [m a] -> m a
+ , mfilter -- :: (MonadPlus m) => (a -> Bool) -> m a -> m a
, filterM -- :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
, mapAndUnzipM -- :: (Monad m) => (a -> m (b,c)) -> [a] -> m ([b], [c])
, zipWithM -- :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]
-- | @'forever' act@ repeats the action infinitely.
forever :: (Monad m) => m a -> m b
+{-# INLINABLE forever #-} -- See Note [Make forever INLINABLE]
forever a = a >> forever a
+{- Note [Make forever INLINABLE]
+
+If you say x = forever a
+you'll get x = a >> a >> a >> a >> ... etc ...
+and that can make a massive space leak (see Trac #5205)
+
+In some monads, where (>>) is expensive, this might be the right
+thing, but not in the IO monad. We want to specialise 'forever' for
+the IO monad, so that eta expansion happens and there's no space leak.
+To achieve this we must make forever INLINABLE, so that it'll get
+specialised at call sites.
+
+Still delicate, though, because it depends on optimisation. But there
+really is a space/time tradeoff here, and only optimisation reveals
+the "right" answer.
+-}
+
-- | @'void' value@ discards or ignores the result of evaluation, such as the return value of an 'IO' action.
void :: Functor f => f a -> f ()
void = fmap (const ())
ap = liftM2 id
+-- -----------------------------------------------------------------------------
+-- Other MonadPlus functions
+
+-- | Direct 'MonadPlus' equivalent of 'filter'
+-- @'filter'@ = @(mfilter:: (a -> Bool) -> [a] -> [a]@
+-- applicable to any 'MonadPlus', for example
+-- @mfilter odd (Just 1) == Just 1@
+-- @mfilter odd (Just 2) == Nothing@
+
+mfilter :: (MonadPlus m) => (a -> Bool) -> m a -> m a
+mfilter p ma = do
+ a <- ma
+ if p a then return a else mzero
+
{- $naming
The functions in this library use the following naming conventions: