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.
