Scalaz state monad examples

前端 未结 3 870
北恋
北恋 2020-12-02 05:12

I haven\'t seen many examples of the scalaz state monad. There is this example but it is hard to understand and there is only one other question on stack overflow it seems.<

3条回答
  •  离开以前
    2020-12-02 05:19

    Here is a very small example on how State can be used:

    Let's define a small "game" where some game units are fighting the boss (who is also a game unit).

    case class GameUnit(health: Int)
    case class Game(score: Int, boss: GameUnit, party: List[GameUnit])
    
    
    object Game {
      val init = Game(0, GameUnit(100), List(GameUnit(20), GameUnit(10)))
    }
    

    When the play is on we want to keep track of the game state, so let's define our "actions" in terms of a state monad:

    Let's hit the boss hard so he loses 10 from his health:

    def strike : State[Game, Unit] = modify[Game] { s =>
      s.copy(
        boss = s.boss.copy(health = s.boss.health - 10)
      )
    }
    

    And the boss can strike back! When he does everyone in a party loses 5 health.

    def fireBreath : State[Game, Unit] = modify[Game] { s =>
      val us = s.party
        .map(u => u.copy(health = u.health - 5))
        .filter(_.health > 0)
    
      s.copy(party = us)
    }
    

    Now we can compose these actions into play:

    def play = for {
      _ <- strike
      _ <- fireBreath
      _ <- fireBreath
      _ <- strike
    } yield ()
    

    Of course in the real life the play will be more dynamic, but it is food enough for my small example :)

    We can run it now to see the final state of the game:

    val res = play.exec(Game.init)
    println(res)
    
    >> Game(0,GameUnit(80),List(GameUnit(10)))
    

    So we barely hit the boss and one of the units have died, RIP.

    The point here is the composition. State (which is just a function S => (A, S)) allows you to define actions that produce results and as well manipulate some state without knowing too much where the state is coming from. The Monad part gives you composition so your actions can be composed:

     A => State[S, B] 
     B => State[S, C]
    ------------------
     A => State[S, C]
    

    and so on.

    P.S. As for differences between get, put and modify:

    modify can be seen as get and put together:

    def modify[S](f: S => S) : State[S, Unit] = for {
      s <- get
      _ <- put(f(s))
    } yield ()
    

    or simply

    def modify[S](f: S => S) : State[S, Unit] = get[S].flatMap(s => put(f(s)))
    

    So when you use modify you conceptually use get and put, or you can just use them alone.

提交回复
热议问题