Dfr
Dfr

Reputation: 4175

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

Upvotes: 2

Views: 2589

Answers (2)

hammar
hammar

Reputation: 139840

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

Upvotes: 2

augustss
augustss

Reputation: 23014

Not all monads support guard; only those in MonadPlus. Looks like you're using the Identity monad (perhaps as part of execState). What would you like to happen when the guard fails?

Upvotes: 1

Related Questions