【问题标题】:Implement repository with State Monad (use IO and State together)使用 State Monad 实现存储库(同时使用 IO 和 State)
【发布时间】:2019-08-09 06:28:39
【问题描述】:

我有这个存储库:

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)

【问题讨论】:

  • 为什么不new TrainService[TrainStateRepository](inMemoryTrainRepository)

标签: scala functional-programming monads scala-cats


【解决方案1】:

你可以引入一个类型参数来抽象你的 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)
}

【讨论】:

  • 感谢弗拉基米尔的回答!它带来了一个难题,但核心问题并未解决。我编辑了我的帖子以添加更多上下文。
猜你喜欢
  • 2019-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多