上一个标题:以全面理解的方式组成DBIO
我不明白,为什么下面的代码甚至无法编译。
我想做什么/上下文
对于电影的售票条目列表中的每个条目,如果在我的数据库中找到了电影,则将其插入。
问题似乎是,我不能在理解中使用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
答案 0 :(得分:3)
是的,这是因为您在理解中使用了不同类型的单子。
考虑未加糖的版本。用于理解的Scala归结为一系列map
和flatMap
的调用。 flatMap
的类型基本上是这样定义的:
def flatMap[F[_], A, B](item: F[A])(fn: A => F[B]): F[B]
请注意,尽管内部类型发生了变化,但是包装类型始终是相同的F类型。在这里,您将DBIO效果类型与Option中的Option进行混合以理解-违反了flatMap的定义。 / p>
在您的情况下,如果您想使整个事情保持for
的理解,可以尝试使用Cats的https://typelevel.org/cats/datatypes/optiont.html的OptionT
monad转换器。 OptionT
本质上提供了一个包装器,使您可以将单价值F[Option[_]]
本身视为单价值。请注意,您还有一个列表,这是第三种单子类型。因此,您的计算可能最终看起来像这样:
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。
不过,您不需要Cats就能做到这一点。您可以在香草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]]
。
希望有帮助!