alvinj / StateMonadExample

This project contains the source code for a State Monad lesson in my book
9 stars 5 forks source link

Weird State monad #2

Open tmichniewski opened 1 year ago

tmichniewski commented 1 year ago

Below I summarize a few observations after studing the examples of State monad.

Firstly, I noticed that when we add some logs to swing, flatMap, and map methods, then we notice that the sequence of operations in for expression and after it is a bit surprising:

Starting for expression
Starting swing
Starting flatMap
// here the for expression is created

Starting run
Starting swing
Starting flatMap
Starting swing
Starting map
// here run is finished

Please notice that out of the whole for expression only the first swing and subsequent flatMap gets called when we execute the for expression.

In contrary, if we rephrase the for expression into direct series of calls to flatMap and map:

  val stateWithNewDistance: State[GolfState, Int] =
    swing(20)
      .flatMap(_ => swing(15))
      .flatMap(_ => swing(0))
      .map(totalDistance => totalDistance)

then we will see that all the calls to swing, flatMap and map are executed instantly while computing stateWithNewDistance. So, there is some mysterious lazy computation involved in the for expression.

Secondly, the name of the monad is really misleading. When we reason about how it may work internally, we end up thinking that we have some value of "state" at hand and after some steps we find ourselve asking: "Hold on, what state are we talking about at the moment, the monad State or the real state of type S represented by case class GolfState". This is a real problem, because there are two "states".

In my opinion, the monad should be renamed to something like StateMonad or StateUpdater or even simply Updater to make this type distinguishable from the real state being updated, which is of generic type S and in the application is represented by GolfState.

Thirdly, I started thinking what in reality we are trying to model by this monad, the real state or the act of updating the state. This is a serious question. This monad seems to pretend that it represents the act of updating the state. So this for expression:

  val stateWithNewDistance: State[GolfState, Int] = for {
    _ <- swing(20)
    _ <- swing(15)
    totalDistance <- swing(0)
  } yield totalDistance

might be treated as series of updating lambda functions like:

val f: Int => Int = (i: Int) => i + 20
val g: Int => Int = (i: Int) => i + 15
val h: Int => Int = (i: Int) => i + 0

but then, this for expression on this mysterious monad might be treated as much simpler composition of those functions:

val fgh: Int => Int = f andThen g andThen h

which could also be later applied like the State monad is applied by calling the run method. And also both (fgh lambda function and State[GolfState, int]) behave kind of lazy. But fgh lambda function, which is also pure, is much, much simpler to reason about and in fact much shorter, as it does not require implementing flatMap and map methods and there is no automatic conversion of for expression into series of flatmap and map calls.

Finally, comming back to the question, whether we want to model the state or the act of updating the state, and if we compare to the monad which was created earlier in the book, when the monad was the class which just encapsulated the Int value, then we could have concluded, that this earlier monad (commented as "What I need is a better State monad") was in fact a monad, which modelled the state, not the act of updating the state.

Moreover, it was pretty obvious what was going on and 100% of Scala developers could understand this code, while the allegedly "better" monad makes understandig of it much more difficult. It may seem initially, that it is more elegant, but when the problems appear (as pointed out at the beginning or if there are more states to update), then it is very difficult to debug it, because there is no Scala library for for expression and transition between for expression and calls to flatMap and map is conducted behind the scene, as there is no trait of Monad (unless somebody uses cats).

Concluding, the "better" State monad is something which makes complex what should be simple and it seems that someone forgot about such rules as KISS ("keep it simple"). Here, insted of 100% we end up in a situation that less than 1% of Scala developers is able to fully understand the concept of State monad. At least in presented version.

alvinj commented 1 year ago

Thank you again for all of this! I’ll reply more as soon as I get to this part of the book during the rewrite process. Really, at this moment I don’t know if I’ll keep this content, but that’s when I’ll take a deep dive into it again.