簡體   English   中英

使用不同的monad進行理解

[英]Using different monads in for-comprehension

可以使用不同的monad進行理解嗎? 這是使用map的代碼

case class Post(id: Int, text: String)

object PostOps {
  def find(id: Int) : Option[Post] = if (id == 1) Some(Post(1, "text")) else None

  def permitted(post: Post, userId: Int) : Try[Post] = if (userId == 1) Success(post) else Failure(new UnsupportedOperationException)

  def edit(id: Int, userId : Int, text: String) = find(id).map(permitted(_, userId).map(_.copy(text = text))) match {
      case None => println("Not found")
      case Some(Success(p)) => println("Success")
      case Some(Failure(_)) => println("Not authorized")
  }
}

直接的for-comprehension版本由於顯而易見的原因不起作用,但它是否可以使其與一些額外的代碼一起使用? 我知道它在C#中是可能的,所以如果它不在Scala中會很奇怪。

你只能在一個理解中使用一種類型的monad,因為它只是flatMapmap語法糖。

如果你有一堆monad(例如Future[Option[A]] )你可以使用monad變換器,但這不適用於此處。

你的案例的一個解決方案可能是使用一個monad:從Option to Try或者從兩個OptionTryEither[String, A]

def tryToEither[L, R](t: Try[R])(left: Throwable => L): Either[L, R] = 
  t.transform(r => Success(Right(r)), th => Success(Left(left(th)))).get

def edit(id: Int, userId: Int, text: String) = {
  val updatedPost = for {
    p1 <- find(id).toRight("Not found").right
    p2 <- tryToEither(permitted(p1, userId))(_ => "Not Authorized").right
  } yield p2.copy(text = text)
  updatedPost match {
    case Left(msg) => println(msg)
    case Right(_)  => println("success")
  }
}

您可以定義錯誤類型而不是使用String ,這樣您就可以使用Either[Error, A]

sealed trait Error extends Exception
case class PostNotFound(userId: Int) extends Error
case object NotAuthorized extends Error 

我猜你的意思是你現在有一個選項[試試[發布]]

find(id).map(permitted(_, userId).map(_.copy(text = text))) match {
  case None => println("Not found")
  case Some(Success(p)) => println("Success")
  case Some(Failure(_)) => println("Not authorized")
}

可以通過幾種方式完成。

嵌套:

  for {
    post <- find(id)
  } yield {
    for {
      tryOfPost <- permitted(post, userId)
    } yield {
      tryOfPost.copy(text = text)
    }
  }

將Option轉換為Try,這樣您就可以使用單一類型,這樣做的缺點是會丟失Try中的錯誤和Option中的None之間的差異。 這里有關於如何從Option到Try的信用。

  for {
    post <- find(id).fold[Try[Post]](Failure[Post](new OtherException))(Success(_))
    permittedPost <- permitted(post, userId)
  } yield {
    permittedPost.copy(text = text)
  }

您還可以在scalaz中查看OptionT monad轉換器以創建一個OptionTTry類型。

但從根本上說,Monads並不是這樣構成的,至少不是一般的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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