我正在开发一个游戏原型,并试图尽可能地保持纯正。所有用例都适合于一个场景-
另一方面,我们必须访问环境(数据库、资源等)、全局游戏状态(不可更改的游戏信任、种子等)。
为了把这一切联系在一起,我最终得到了这样的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
}服务方法返回类型-通过读取器提供对环境的访问,通过Writer生成一些输出,提供对GameState的访问,并生成方法结果错误或某些类型
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)所以,我的问题是:
看起来我可以试着用变压器,但我的头也不能绕过去。
谢谢!
发布于 2017-07-20 19:00:31
使用ReaderWriterStateT
type Result[T] = ReaderWriterStateT[Either[Error, ?], Environment, List[Output], State, T]这相当于
(Environment, State) => Either[Error, (List[Output], T, State)]这也意味着在发生错误时,不会写入输出,也不会更改状态。
如果您确实希望保持与您拥有的Result结构相同的结构,请使用
type Result[T] = EitherT[ReaderWriterState[Environment, List[Output], State, ?], Error, T]发布于 2017-07-27 09:09:27
由于@tomas,它在左侧类型的情况下工作和退出。
以下是结果代码:
type ErrorOr[+T] = Error \/ T
type Result[T] = ReaderWriterStateT[ErrorOr, Environment, List[Output], State, T]
object UserServiceImpl extends UserService {
def findPlayer(id: Long): Result[Player] = ReaderWriterStateT { (env, state) =>
val player = Player(1L, "name")
\/-((List.empty[Output], player, state))
}
def updatePlayer(player: Player): Result[Player] = ReaderWriterStateT { (env, state) =>
\/-((List.empty[Output], player.copy(name = "updated"), state))
}
}
val userService = UserServiceImpl
val result = (for {
player <- userService.findPlayer(1L)
updated <- userService.updatePlayer(player)
} yield updated).run(GameEnvironment, GameState)
result match {
case \/-((out, player, state)) => println(player)
case -\/(error) => println(error)
}https://stackoverflow.com/questions/45218294
复制相似问题