scala - 使用 State Monad 实现存储库(同时使用 IO 和 State)
问题描述
我有这个存储库:
trait TrainRepository {
def get(trainId: TrainId): IO[Option[Train]]
def getAll: IO[List[Train]]
def save(train: Train): IO[Train]
}
我想提供一个使用 State Monad 的内存实现。但我被困住了:
- 如果我扩展 trait,我会被类型卡住,
IO[...]
所以我相信我将无法使用 State Monad。 - 我需要使用自然变换吗?
- 我需要使用 Free Monad 吗?(我宁愿不要)
你会怎么做?
编辑以提供更多背景信息:
trait TrainRepository[F[_]] {
def get(trainId: TrainId): F[Option[Train]]
def save(train: Train): F[Train]
}
class TrainService[F[_]](repository: TrainRepository[F])(implicit monad: Monad[F]) {
def reservation(id: TrainId): F[Train] =
for{
train <- repository.get(id)
updatedTrain <- train match {
case None => monad.pure("test") // return error here
case Some(train) => monad.pure(train.bookSeat)
}
_ <- repository.save(updatedTrain)
} yield updatedTrain
}
type TrainStateRepository[A] = State[Map[TrainId, Train], A]
val inMemoryTrainRepository = new TrainRepository[TrainStateRepository] {
override def get(trainId: TrainId): TrainStateRepository[Option[Train]] = ???
override def save(train: Train): TrainStateRepository[Train] = ???
}
val postgresTrainRepository = new TrainRepository[IO] {
override def get(trainId: TrainId): IO[Option[Train]] = ???
override def save(train: Train): IO[Train] = ???
}
val testTrainService = new TrainService[IO](inMemoryTrainRepository)
// The error is here ^^^^
// I cannot mix IO and State
val prodTrainService = new TrainService[IO](postgresTrainRepository)
解决方案
您可以引入类型参数以抽象您的 monad:
trait TrainRepository[F[_]] {
def get(trainId: TrainId): F[Option[Train]]
def getAll: F[List[Train]]
def save(train: Train): F[Train]
}
然后你的 state monad 实现看起来像
type TrainsState[A] = State[Map[TrainId, Train], A]
class StateTrainRepository extends TrainRepository[TrainsState] {
override def get(trainId: TrainId): TrainsState[Option[Train]] = State.inspect(_.get(trainId))
override def getAll: TrainsState[List[Train]] = State.inspect(_.values.toList)
override def save(train: Train): TrainsState[Train] =
State.modify[Map[TrainId, Train]](m => m + (train.id -> train)) *> State.pure(train)
}
推荐阅读
- mysql - 创建适用于 MySQL 中不同表的过程/函数
- listview - 滚动侦听器不适用于 SingleChildScrollView Flutter 中的列表视图
- android - 文件名中的红色是否意味着某处有错误?
- c# - 如何更改 WPF 中所有列表视图项的属性?
- javascript - 使用 Bootstrap 4 进行全宽扩展的可扩展网格
- android - 无法保存 sharedPreference 状态
- powershell - 需要使用 powershell 从 windows server 导出和导入证书
- nginx - 如何向两台服务器请求然后用 nginx 返回响应?
- python-3.x - Python中大型数据集的曲线拟合
- ios - 由于 Transporter,我的应用程序不会上传。我怎样才能绕过这个?