I see here
-- Note that "forever" isn't necessarily non-terminating.
-- If the action is in a @'MonadPlus'@ and short-circuits after some number of iterations.
-- then @'forever'@ actually returns `mzero`, effectively short-circuiting its caller.
To be honest I don't understand this note. Do they mean that it is possible to break forever
with MonadPlus
, for instance - IO Bool
? Let's say, IO False
will break it...
From one point of view IO
is MonadPlus
too. Maybe I must wrap my IO Bool
in something else to achieve the possibility to break forever
with IO Bool
and MonadPlus
? What does the note mean at all?
Sure, I can break it with exception or to implement own forever
but my interest is about this strange note.
CodePudding user response:
You can look at how forever
is implemented:
forever :: Applicative f => f a -> f b -> f b
forever a = let a' = a *> a' in a'
The documentation of (*>)
says that it "sequence actions, discarding the value of the first argument". It basically is a (>>)
for Applicatives instead of Monads.
So if you look at how forever
is implemented you'll see that it basically is expanded to:
forever a = a *> a *> a *> ...
As the forever
description says, if the Applicative has some short-circuiting behaviour it can still terminate and not evaluate the infinite sequence of actions:
ghci> forever $ Nothing
Nothing
ghci> forever $ Just 1
-- infinite loop trying to evaluate Just 1 *> Just 1 *> Just 1 *> ...
That is because (Nothing *> _) = Nothing
what follows the (*>)
is not even evaluated, so Nothing *> Nothing *> Nothing *> ...
short-circuits to Nothing
without having to evaluate the infinite list of actions.
CodePudding user response:
One may naively assume that forever m
just goes on forever:
forever m = m >> forever m
= m >> m >> forever m
= m >> m >> m >> ... -- forever
But the comment mentions that there are ways to break the loop, and mzero
is a concise example, that demonstrates the situation equationally rather than by thinking about exceptions operationally. mzero
satisfies mzero >> w = mzero
for all w
, so that:
forever mzero = mzero >> forever mzero
= mzero
The point is that the choice of monad makes forever
much more versatile than a mere while (true)
loop in imperative languages.