簡體   English   中英

全面理解多種不同的monad類型

[英]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歸結為一系列mapflatMap調用。 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本質上提供了一個包裝器,使您可以將OptionTF[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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM