2

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
duplode
  • 33,731
  • 7
  • 79
  • 150
Dfr
  • 4,075
  • 10
  • 40
  • 63
  • In what monad do you run this? – fuz Apr 04 '11 at 17:07
  • I talked about the monad inside execState. – fuz Apr 04 '11 at 18:07
  • 1
    It's actually '5' and you can get it if you swap transformers: `(\`execState\`1) . runMaybeT . forever $ do { modify (+1); x <- get; guard $ x < 5 }` – Ed'ka Apr 04 '11 at 23:04
  • 1
    @Ed'ka it would be cool if you give type signatures for swapped variant, because i can not understand how haskell infers so complex type by its own. – Dfr Apr 05 '11 at 04:20

2 Answers2

2

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
hammar
  • 138,522
  • 17
  • 304
  • 385
  • 1
    `modify (+1) :: (MonadState s m, Num s) => m ()` So, you are passing () to the lambda, and trying to compare it to 5, which fails since () is not an instance of Num. – pat Apr 05 '11 at 17:07
  • @pat: Thanks. Could've sworn `modify` returned the new value ;) I've edited my answer. – hammar Apr 20 '11 at 15:53
1

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?

augustss
  • 22,884
  • 5
  • 56
  • 93
  • Hi, i expect when guard fails, then body loops until guard and when guard succeeds, then looping is finished – Dfr Apr 04 '11 at 18:04