Search code examples
haskellabstractionmonadsboilerplatestate-monad

State monads: trading one pattern for another?


So I'm writing a game in Haskell, and I'm expressing a player's turn as a series of state-altering functions that correlate to various turn phases. Originally, this looks something like:

let game'  = phase1 game
    game'' = phase2 game'
-- etc.

Prime candidate for State monadosity, right? This leads to the more elegant:

do
  phase1
  phase2
-- etc.

However, then it seems like I have to change phase1, phase2, et al to begin with a boilerplate "State getting" step:

phase1 = get >>= \game -> -- ...

I'm hoping there's a way to abstract this out, so I can avoid boilerplate on both the caller and the callee. I'm just too new to know what this way is (this is my first real Haskell project). Any advice?


Solution

  • Well, it's not quite monadosic yet. This is a prime candidate for an Endo monoid. This leads to the more elegant

    game = mconcat [ phase1, phase2, ... ]
    

    And each phase is written:

    phase1 = Endo $ \game -> ...
    

    You would move to a monad if you needed to return a some additional data along with the new state in each phase. In that case a simple function will make your boilerplate more tolerable:

    phase :: (GameState -> GameMonad a) -> GameMonad a
    phase f = f =<< get
    

    And then a phase is written:

    phase1 = phase $ \game -> do ...
    

    But if you want to use the state, you are probably going to have to give it a name (unless you can finagle pointfreeness by, say, using gets or data-accessor), and in that case you can't get much terser than a function and a lambda.