scala - 在 for-comprehension 中使用不同的 monad

标签 scala monads

可以在 for-comprehensions 中使用不同的 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 中会很奇怪。

最佳答案

你只能在 a 中使用一种类型的 monad 来理解,因为它只是 flatMap 的语法糖。和 map .

如果您有一堆 monad(例如 Future[Option[A]] ),您可以使用 monad 转换器,但这不适用于此处。

针对您的情况的解决方案可能是使用一个 monad:从 Option 开始至 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 

关于scala - 在 for-comprehension 中使用不同的 monad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33401077/

相关文章:

scala - 在 Scala 中处理多个 Future

haskell - 交换内部和外部单子(monad)

exception - Haskell 中的安全应用

c# - 异常的三元运算符?其他选择?

haskell - 在链的末尾惯用地返回一个 Maybe

scala - 如何将 RDD[Row] 转换回 DataFrame

scala - 为每一行查找一组列中的第一个非空值和列名

scala - 验证列表 Scala 中的项目的好方法

scala - 在连续 block 中读取非常大的文件 (~ 1 TB)

haskell - 从 monad 中取出 monadic 函数