Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 963
J Cooper Avatar asked Feb 27 '23 12:02

J Cooper


1 Answers

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.

like image 165
luqui Avatar answered Mar 04 '23 09:03

luqui