5

I have a recursive function that receives a data object with number of fields, something like:

data MyState = {
  first  :: Int,
  second :: String,
  third  :: Bool,
  ... 
}

type Result = Int

This recursive function changes it during the execution and passes it to itself for the next execution. It looks like following:

-- This is a pseudocode, just to give an idea about the workflow. 
process :: MyState -> Result
process st = go st
   where go st | first == 1 = (go . changeFunc1) st
               | first == 2 = (go . changeFunc2) st
               | otherwise  = generateResult st

changeFunc1 :: MyState -> MyState
changeFunc1 st | third st == True && second st == "abc" = st {first = inc}
               | otherwise  = st
   where inc = first st + 1

...

In this case, instead of passing MyState parameter between functions I could use State monad and I would need to wrap/unwrap monad all the time. I am relatively new to Haskell and need a recommendation, which approach is better in my case. Thanks.

Sergii Sopin
  • 411
  • 2
  • 11
  • 11
    Passing state like this as a parameter is very common. No, you don't have to use a monad if you don't think you need one. – Fyodor Soikin Jan 21 '20 at 17:32
  • Thanks! I am still a bit confused about when it does make sense to use monads, but it seems that I just need to follow a common sense. – Sergii Sopin Jan 21 '20 at 17:34
  • 7
    When you need to modify `MyState`, only, there's little reason to use a monad. Composing such effects can be done by function composition, and that's easy. The issue is when you want both 1) to modify the state and 2) to return another value. Then, you need `MyState -> (MyState, Result)`, and that does not compose as easily. The monadic approach is not strictly needed (one can define its own composition routine), but it is nice to have, especially after becoming familiar with monads. – chi Jan 21 '20 at 18:03
  • 2
    Also wrapping/unwrapping is free. Newtype constructors are erased at compile time. – luqui Jan 21 '20 at 18:04
  • 2
    Despite the name, the `State` monad is not about representing state, but about representing state *changes* in a way that makes it easier to chain them together without considering the initial starting state. `changeFunc1, changeFunc2 :: State MyState ()` make sense, but `process` might actually be simpler as-is. – chepner Jan 21 '20 at 18:55
  • @luqui You are right. It seems I misinterpreted following statement from "Real world Haskell" book: "It's easy to forget this point: when a computation in the chain fails, the subsequent production, chaining, and consumption of Nothing values is cheap at runtime, but it's not free." – Sergii Sopin Jan 21 '20 at 20:22

0 Answers0