【发布时间】:2017-07-20 14:54:14
【问题描述】:
我正在制作游戏原型,并尝试尽可能地纯粹。 所有用例都适合一个场景 -
- 尝试在存储中查找播放器
- 执行一些业务逻辑
- 更新存储中的播放器
- 虽然更新一个可以产生一些输出 - 日志消息,消息给其他玩家等。
从另一方面来说,必须访问环境(数据库、资源等)、全局游戏状态(不可变的游戏配置、种子等)。
为了将这一切联系在一起,我最终得到了这样的 scalaz7 ReaderWriterState monad:
一些定义:
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
}
服务方法返回类型 - 通过 Reader 提供对 Environment 的访问,通过 Writer 产生一些输出,提供对 GameState 的访问并产生方法结果 - Error 或 Some type
type Result[T] = ReaderWriterState[Environment, List[Output], State, Error \/ T]
只是一个关于如何实现服务的示例
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
)
}
}
上述场景是(不会编译):
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)
所以,我的问题是:
- 如果 findPlayer:RWS 也返回左,如何短路?
- 如何将 playerOrError 转换为右投影并在此处传递?
看起来我可以尝试以某种方式使用转换器,但无法理解它。
谢谢!
【问题讨论】:
标签: scala scalaz monad-transformers