if-statementhaskellmonadsstate-monaddo-notation

Conditional change of State in Haskell


I don't know how I can make a conditional change to State Monad in Haskell. Suppose, I have a stack on State Monad.

import Control.Monad.State

push :: Int -> State [Int] ()
push x = state $ \xs -> ((), x : xs)

pop :: State [Int] Int
pop = state $ \(x : xs) -> (x, xs)

And with that, I want to write a function changing it.

someFunc :: Bool -> Int -> State [Int] ()
someFunc b x = do 
  let someValue = x * x
  if b then
    p <- pop
    someValue = someValue + p
  push $ someValue

For example, I want to take a bool b and a value x. And if b is True (for example, it could be a condition that my stack is not empty) I want to pop a value from my stack and add it to some variable. Otherwise, I don't add anything to the variable and just push it into the stack.

How can I achieve this kind of behaviour?


Solution

  • In Haskell, every if needs an else, since everything is just a value. Also, you can't do if ... then p <- pop ... since the do notation is lost in an if statement, so you need to restart it with if ... then do p <- pop ....

    import Control.Monad.State
    
    push :: a -> State [a] ()
    push x = state $ \xs -> ((), x : xs)
    
    pop :: State [a] a
    pop = state $ \(x : xs) -> (x, xs)
    
    someFunc :: (Num a) => Bool -> a -> State [a] ()
    someFunc b x = do
      let baseValue = x * x
      someValue <- if b then do
            p <- pop
            return $ baseValue + p
          else do
            return baseValue
      push $ someValue