How to stack the State and IO monads

Viewed 353

In a game of Connect4:

  • we start with an empty grid
  • two players place pieces x and o on the grid
  • the first player to achieve 4 pieces in a line wins!
  • this is a text based console game

My thinking is that:

  • at each step of the game the Grid is transitioning from one state to another
  • hence i need to use the State monad
  • and because this is a console based app that involves io
  • in this case I also need to use the IO monad

Is this thinking correct?

Assuming the above is correct, which one of these is correct?

  • type StateInIO[S,A] = IO[State[S,A]]
  • type IOInState[S,A] = State[S,IO[A]]

I favour the second option, makes more sense to me.

Can I stack these monads (State, IO) in this way?

2 Answers

State in cats is actually defined as type alias for monad transformer StateT[F[_], S, A] where F[_] is effect type. This alias fixes F[_] with Eval, so it looks like type State[S, A] = StateT[Eval, S, A]. In your case, you should just define your own IO state like type IOState[S, A] = StateT[IO, S, A].

StateT[IO, S, A] corresponds to IO[S => IO[(S, A)]] and I feel it is more useful stack because you can now just easily connect code using this stack with rest of your application using IO.

You can find more on stacking state monad with effect monad with StateT in section interleaving effects in the documentation of State from cats.

I think you should just write a working program and generalize/refactor it afterwards instead of overingeneering upfront. You need State[S, A] for business logic and IO[A] for interacting with the console. But you don't have to mix it.

Then in the main class where you write a user-interaction loop you could leverage StateT which unifies both of your types.

By the way IO[State[S,A]] doesn't look like a very useful type. It says that you can read State[S, A] from the real world. State is a function. There are no sensible ways to read a function from the real world. So most probably you need something less powerful. Of course this type makes sense in the context of monad transformers.

Related