【问题标题】:Short-circuit from ReaderWriterState[Either]来自 ReaderWriterState[Either] 的短路
【发布时间】:2017-07-20 14:54:14
【问题描述】:

我正在制作游戏原型,并尝试尽可能地纯粹。 所有用例都适合一个场景 -

  1. 尝试在存储中查找播放器
  2. 执行一些业务逻辑
  3. 更新存储中的播放器
  4. 虽然更新一个可以产生一些输出 - 日志消息,消息给其他玩家等。

从另一方面来说,必须访问环境(数据库、资源等)、全局游戏状态(不可变的游戏配置、种子等)。

为了将这一切联系在一起,我最终得到了这样的 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)

所以,我的问题是:

  1. 如果 findPlayer:RWS 也返回左,如何短路?
  2. 如何将 playerOrError 转换为右投影并在此处传递?

看起来我可以尝试以某种方式使用转换器,但无法理解它。

谢谢!

【问题讨论】:

    标签: scala scalaz monad-transformers


    【解决方案1】:

    使用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]
    

    【讨论】:

    • 谢谢,我试试。做 '?'用特殊的编译器插件替换 lamda 类型?我在玩 lambdas 类型和这个任务但失败了
    • 哦,是的,? 来自 kind projector
    【解决方案2】:

    感谢@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)
    }
    

    【讨论】:

      猜你喜欢
      • 2021-01-04
      • 2015-11-30
      • 1970-01-01
      • 2011-02-24
      • 1970-01-01
      • 1970-01-01
      • 2019-10-09
      • 2019-04-03
      • 1970-01-01
      相关资源
      最近更新 更多