[英]Composing multiple different monad types in a for-comprehension
上一個標題: 全面理解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
是的,這是因為您在理解中使用了不同類型的單子。
考慮一下未加糖的版本。 用於理解的Scala歸結為一系列map
和flatMap
調用。 flatMap
的類型基本上是這樣定義的:
def flatMap[F[_], A, B](item: F[A])(fn: A => F[B]): F[B]
請注意,當內部類型發生變化時,包裝類型始終是相同的F類型。在這里,您將DBIO效果類型與Option混合使用,以理解-違反了flatMap的定義。
在你的情況,如果你想保持整個事情中for
理解,你可以嘗試OptionT
從貓單子轉換: https://typelevel.org/cats/datatypes/optiont.html 。 OptionT
本質上提供了一個包裝器,使您可以將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]]
。
希望有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.