Option[F[ShoppingCart]] 到 F[Option[ShoppingCart]]

问题描述

我在 Scala 中有以下代数(我使用的是无标签最终模式):

trait ShoppingCarts[F[_]] {
  def create(id: String): F[Unit]
  def find(id: String): F[Option[ShoppingCart]]
  def add(sc: ShoppingCart,product: Product): F[ShoppingCart]
}

使用上述代数,我创建了以下程序:

def createAndToCart[F[_] : Monad : ShoppingCarts](product: Product,cartId: String): F[Option[ShoppingCart]] =
  for {
    _ <- ShoppingCarts[F].create(cartId)
    maybeSc <- ShoppingCarts[F].find(cartId)
    maybeNewScF = maybeSc.map(sc => ShoppingCarts[F].add(sc,product))
    maybeNewSc <- maybeNewScF match {
      case Some(d) => d.map(s1 => Option.apply(s1))
      case _ => Monad[F].pure(Option.empty[ShoppingCart])
    }
  } yield maybeNewSc

我不太喜欢 for-comprehension 结构中将 Option[F[ShoppingCart]] 转换为 F[Option[ShoppingCart]] 的代码。我确信我可以做得更好,但我不知道如何改进。

我正在使用猫。

解决方法

您正在寻找 traversesequence。这些函数所做的是“切换”效果的顺序,因此如果 G 有 G[F[A]] 的实例,它们可以将 F[G[A]] 更改为 ApplicativeOption 的作用域中有这样一个实例,因此您可以使用它。

traverse 需要额外的映射函数,但如果你只是想“切换”效果,那么 sequence 将是一条路:

for {
      _ <- ShoppingCarts[F].create(cartId)
      maybeSc <- ShoppingCarts[F].find(cartId)
      maybeNewSc <- maybeSc.map(sc => ShoppingCarts[F].add(sc,product)).sequence //here you need to use sequence
} yield maybeNewSc

或者你可以用 mapsequencetraverse 合并为一个步骤:

for {
      _ <- ShoppingCarts[F].create(cartId)
      maybeSc <- ShoppingCarts[F].find(cartId)
      maybeNewSc <- maybeSc.traverse(sc => ShoppingCarts[F].add(sc,product))
} yield maybeNewSc

您可以在 cats docs 中阅读有关 sequencetraverse 的更多信息。

我建议您检查的另一件事是 monad transformers,因为它们使处理诸如 F[Option[A]] 之类的嵌套 monad 堆栈变得更加容易。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...