在 for-comprehension 中组合多个不同的 monad 类型
Composing multiple different monad types in a for-comprehension
Previous Title: Composing DBIOs in for-comprehension
我不明白,为什么下面的代码甚至不能编译。
我想做什么/背景
对于电影门票销售条目列表中的每个条目,如果在我的数据库中找到该电影,则将其插入。
问题似乎是,我不能在 for-comprehensions 中使用 DBIO。这是为什么?是因为我在同一个地方使用了不同类型的单子来理解吗?
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
val insertMetricActions: List[DBIO[UUID]] = for {
movieTicketSaleNumber: MovieTicketSale <- movieTicketSaleNumbers
isInDatabaseAction: DBIO[Option[Movie]] = moviesDb.findOneExact(movieTicketSaleNumber.movie.id)
optionalMovie: Option[Movie] <- isInDatabaseAction
movieInDatabase: Movie <- optionalMovie
insertMovieNumbersInDatabaseAction: DBIO[UUID] = insertMovieTicketSale(movieTicketSaleNumber, movieInDatabase)
movieNumberDbId: UUID <- insertMovieNumbersInDatabaseAction
} yield movieNumberDbId
编译器输出:
[error] found : slick.dbio.DBIOAction[java.util.UUID,slick.dbio.NoStream,slick.dbio.Effect.All]
[error] required: Option[?]
[error] movieNumberDbId: UUID <- insertMovieNumbersInDatabaseAction
[error] ^
[error] [PROJECTPATHPLACEHOLDER]: type mismatch;
[error] found : Option[Nothing]
[error] required: slick.dbio.DBIOAction[?,?,?]
[error] movieInDatabase: Movie <- optionalMovie
[error] ^
[error] [PROJECTPATHPLACEHOLDER]: type mismatch;
[error] found : slick.dbio.DBIOAction[Nothing,Nothing,slick.dbio.Effect.All with slick.dbio.Effect]
[error] required: scala.collection.GenTraversableOnce[?]
[error] optionalMovie: Option[Movie] <- isInDatabaseAction
[error] ^
[error] three errors found
[error] (Compile / compileIncremental) Compilation failed
是的,这是因为您在 for 理解中使用了不同类型的 monad。
想想不加糖的版本。 Scala for comprehensions 归结为一系列 map
和 flatMap
调用。 flatMap
的类型基本上是这样定义的:
def flatMap[F[_], A, B](item: F[A])(fn: A => F[B]): F[B]
请注意,虽然内部类型发生变化,但包装类型始终是同一类型 F。在这里,您将 DBIO 效果类型与相同的 Option 混合在一起以进行理解——这违反了 flatMap 的定义.
在你的情况下,如果你想让整个事情保持在 for
理解中,你可以尝试来自 Cats 的 OptionT
monad 转换器:https://typelevel.org/cats/datatypes/optiont.html。 OptionT
本质上提供了一个包装器,允许您将 monadic 值 F[Option[_]]
本身视为 monadic 值。请注意,您还有一个 List,这是第三种 monadic 类型。所以你的计算最终可能看起来像:
import cats._
import cats.data._
import cats.implicits._
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
def insertTicket(sale: MovieTicketSale): OptionT[DBIO, UUID] =
for {
movie <- OptionT(moviesDb.findOneExact(sale.movie.id))
movieNumberDbId <- OptionT.liftF(insertMovieTicketSale(sale, movie))
} yield movieNumberDbId
val insertMetricActions: List[DBIO[Option[UUID]]] = movieTicketSaleNumbers.map(insertTicket(_).value)
这将为您提供包含已插入的可选 UUID 的效果列表。
虽然你不需要猫来做这件事。你可以在 vanilla Scala 中做你想做的事,尽管它有点笨拙:
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
def insertTicket(sale: MovieTicketSale): DBIO[Option[UUID]] =
for {
movie <- moviesDb.findOneExact(sale.movie.id)
movieNumberDbId <- movie.map(insertMovieTicketSale(sale, _).map(Option(_))).getOrElse(DBIO.successful(None))
} yield movieNumberDbId
val insertMetricActions: List[DBIO[Option[UUID]]] = movieTicketSaleNumbers.map(insertTicket(_))
可能有更优雅的表达方式,尤其是将 Option[DBIO[UUID]]
转换为 DBIO[Option[UUID]]
。
希望对您有所帮助!
Previous Title: Composing DBIOs in for-comprehension
我不明白,为什么下面的代码甚至不能编译。
我想做什么/背景
对于电影门票销售条目列表中的每个条目,如果在我的数据库中找到该电影,则将其插入。
问题似乎是,我不能在 for-comprehensions 中使用 DBIO。这是为什么?是因为我在同一个地方使用了不同类型的单子来理解吗?
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
val insertMetricActions: List[DBIO[UUID]] = for {
movieTicketSaleNumber: MovieTicketSale <- movieTicketSaleNumbers
isInDatabaseAction: DBIO[Option[Movie]] = moviesDb.findOneExact(movieTicketSaleNumber.movie.id)
optionalMovie: Option[Movie] <- isInDatabaseAction
movieInDatabase: Movie <- optionalMovie
insertMovieNumbersInDatabaseAction: DBIO[UUID] = insertMovieTicketSale(movieTicketSaleNumber, movieInDatabase)
movieNumberDbId: UUID <- insertMovieNumbersInDatabaseAction
} yield movieNumberDbId
编译器输出:
[error] found : slick.dbio.DBIOAction[java.util.UUID,slick.dbio.NoStream,slick.dbio.Effect.All]
[error] required: Option[?]
[error] movieNumberDbId: UUID <- insertMovieNumbersInDatabaseAction
[error] ^
[error] [PROJECTPATHPLACEHOLDER]: type mismatch;
[error] found : Option[Nothing]
[error] required: slick.dbio.DBIOAction[?,?,?]
[error] movieInDatabase: Movie <- optionalMovie
[error] ^
[error] [PROJECTPATHPLACEHOLDER]: type mismatch;
[error] found : slick.dbio.DBIOAction[Nothing,Nothing,slick.dbio.Effect.All with slick.dbio.Effect]
[error] required: scala.collection.GenTraversableOnce[?]
[error] optionalMovie: Option[Movie] <- isInDatabaseAction
[error] ^
[error] three errors found
[error] (Compile / compileIncremental) Compilation failed
是的,这是因为您在 for 理解中使用了不同类型的 monad。
想想不加糖的版本。 Scala for comprehensions 归结为一系列 map
和 flatMap
调用。 flatMap
的类型基本上是这样定义的:
def flatMap[F[_], A, B](item: F[A])(fn: A => F[B]): F[B]
请注意,虽然内部类型发生变化,但包装类型始终是同一类型 F。在这里,您将 DBIO 效果类型与相同的 Option 混合在一起以进行理解——这违反了 flatMap 的定义.
在你的情况下,如果你想让整个事情保持在 for
理解中,你可以尝试来自 Cats 的 OptionT
monad 转换器:https://typelevel.org/cats/datatypes/optiont.html。 OptionT
本质上提供了一个包装器,允许您将 monadic 值 F[Option[_]]
本身视为 monadic 值。请注意,您还有一个 List,这是第三种 monadic 类型。所以你的计算最终可能看起来像:
import cats._
import cats.data._
import cats.implicits._
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
def insertTicket(sale: MovieTicketSale): OptionT[DBIO, UUID] =
for {
movie <- OptionT(moviesDb.findOneExact(sale.movie.id))
movieNumberDbId <- OptionT.liftF(insertMovieTicketSale(sale, movie))
} yield movieNumberDbId
val insertMetricActions: List[DBIO[Option[UUID]]] = movieTicketSaleNumbers.map(insertTicket(_).value)
这将为您提供包含已插入的可选 UUID 的效果列表。
虽然你不需要猫来做这件事。你可以在 vanilla Scala 中做你想做的事,尽管它有点笨拙:
val movieTicketSaleNumbers: List[MovieTicketSale] = cinemaApi.allMovieTicketSales
def insertTicket(sale: MovieTicketSale): DBIO[Option[UUID]] =
for {
movie <- moviesDb.findOneExact(sale.movie.id)
movieNumberDbId <- movie.map(insertMovieTicketSale(sale, _).map(Option(_))).getOrElse(DBIO.successful(None))
} yield movieNumberDbId
val insertMetricActions: List[DBIO[Option[UUID]]] = movieTicketSaleNumbers.map(insertTicket(_))
可能有更优雅的表达方式,尤其是将 Option[DBIO[UUID]]
转换为 DBIO[Option[UUID]]
。
希望对您有所帮助!