3

我在 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

我不太喜欢将 an转换Option[F[ShoppingCart]]F[Option[ShoppingCart]]. 我确信我可以做得更好,但我不知道如何改进它。

我正在使用猫。

4

1 回答 1

4

您正在寻找traversesequence。这些函数所做的是“切换”效果的顺序,因此G[F[A]]如果F[G[A]]G 有Applicative. Option范围内有这样的实例,因此您可以使用它。

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

或者您可以合并mapsequence进入一个步骤traverse

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

您可以阅读更多关于猫文档sequence的内容。traverse

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

于 2021-03-07T15:27:50.167 回答