2017-07-20 21 views
1

我正在研究一個遊戲原型,並試圖儘可能地純粹。 所有用例都放入一個場景 -來自ReaderWriterState的短路[任一]

  1. 試圖找到播放器在存儲
  2. 在存儲
  3. 執行一些業務邏輯
  4. 更新玩家在更新一個能產生一定的輸出 - 日誌消息,轉發到其他播放器等

從另一個側面一個已經拿到進入環境(數據庫,資源等),全球遊戲狀態(不可變的遊戲CONFIGS,種子等等)。

爲了配合它一起我結束了scalaz7 ReaderWriterState單子是這樣的:

一些定義:

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) 

所以,我的問題是:

  1. 如何findPlayer:RWS返回左邊?
  2. 如何將playerOrError轉換爲正確的投影並將其傳遞給此處?

它看起來像我可以嘗試使用變壓器,但無法繞過它。

謝謝!

回答

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] 
+0

謝謝,我會嘗試。是'?'用特殊的編譯器插件替換類型lamda?我正在玩類型lambda和這項任務,但失敗 –

+1

哦,是的,'?'來自[種類投影機](https://github.com/non/kind-projector/)。 –

0

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