haskellmonadplus

Haskell State monad and monadic guard


i have small task to emulate imperative loop in monadic code with state involved and there should be no IO, the task is to exit loop on condition and here is my attempt:

> execState (forever $ modify (+1) >>= \x -> guard $ x < 5 ) 1

So i expect to get something like 4 here, but instead getting this cryptic message:

<interactive>:1:43:
    No instance for (MonadPlus Data.Functor.Identity.Identity)
      arising from a use of `guard' at <interactive>:1:43-47
    Possible fix:
      add an instance declaration for
      (MonadPlus Data.Functor.Identity.Identity)
    In the first argument of `($)', namely `guard'
    In the expression: guard $ x < 5
    In the second argument of `(>>=)', namely `\ x -> guard $ x < 5'

Whole thing works ok without guard, but it seems completely hate guard for some reason.

UPD: Finally i got it running, thanks to hammar for types hint. Despite it returns Nothing, i know it runs 5 times and this is cool, not sure how it can be useful for now.

runStateT (forever $ do { modify (+1); x <- get; guard $ x < 5 } :: StateT Int Maybe Int) 1

Solution

  • As the error message is trying to tell you, guard requires that the monad you're using must be an instance of the type class MonadPlus.

    In this example, you're using the State monad, which is actually a StateT transformer on top of the Identity monad. There is a MonadPlus instance for StateT, but it requires that the underlying monad must be a MonadPlus as well, which Identity isn't.

    For your example, you can try something like

    > let loop = modify (+1) >> get >>= \x -> if x < 5 then loop else return x in execState loop 1