I'm working on a game prototype and trying to be as pure as possible. All use-cases are fit into one scenario -
- Try to find player in a storage
- Perform some business logic
- Update player in the storage
- While updating one can produce some output - Log messages, Messages to another players, etc.
From another side one have to get access to Environment (databases, resources, etc), global game state (immutable game configs, seeds etc).
To tie it all together I ended up with scalaz7 ReaderWriterState monad like this:
Some definitions:
trait UserService
trait Environment
trait State
sealed trait Error
sealed trait Output
case object GameEnvironment extends Environment
case object GameState extends State
object Output {
case object Log extends Output
case object Parcel extends Output
case object Analytics extends Output
}
object Error {
case class AppError(code: String) extends Error
case class ThrowableError(ex: Exception) extends Error
}
Service methods return type - provides access to Environment via Reader, produces some output via Writer, gives access to GameState and produces method result - Error or Some type
type Result[T] = ReaderWriterState[Environment, List[Output], State, Error \/ T]
Just an example on how Service might be implemented
object UserServiceImpl extends UserService {
def findPlayer(id: Long): Result[Player] = ReaderWriterState { (env, state) =>
(
Nil,
\/-(Player(id, "name")),
state
)
}
def updatePlayer(player: Player): Result[Player] = ReaderWriterState { (env, state) =>
(
List(Output.Log),
\/-(player.copy(name = "updated")),
state
)
}
}
Above mentioned scenario is (won't compile):
val (out, res, state) = (for {
playerOrError <- userService.findPlayer(1L) //How to short-circuit if findPlayer returns left either?
updated <- userService.updatePlayer(playerOrError) //How to transform playerOrError to right projection and pass it here?
} yield player).run(GameEnvironment, GameState)
So, my questions are:
- How to short-circuit if findPlayer:RWS returns left either?
- How to transform playerOrError to right projection and pass it here?
It looks like I can try to use transformer somehow but can't get my head around it.
Thanks!