在Scala中使用理解,尝试和排序 [英] Using for-comprehension, Try and sequences in Scala

查看:83
本文介绍了在Scala中使用理解,尝试和排序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您有很多方法:

def foo() : Try[Seq[String]]
def bar(s:String) : Try[String]

并且您想理解一下:

for {
  list <- foo
  item <- list
  result <- bar(item)
} yield result

当然不会编译,因为在这种情况下Seq不能与Try一起使用.

任何人都有一个不错的解决方案,如何在不将其分成两个for的情况下将其写得干净整洁?

我已经第三次遇到这个语法问题了,以为是时候问这个问题了.

解决方案

恕我直言: Try Seq 远远超出了定义monad转换器的需要:

库代码:

case class trySeq[R](run : Try[Seq[R]]) {
  def map[B](f : R => B): trySeq[B] = trySeq(run map { _ map f })
  def flatMap[B](f : R => trySeq[B]): trySeq[B] = trySeq {
    run match {
      case Success(s) => sequence(s map f map { _.run }).map { _.flatten }
      case Failure(e) => Failure(e)
    }
  }

  def sequence[R](seq : Seq[Try[R]]): Try[Seq[R]] = {
    seq match {
      case Success(h) :: tail =>
        tail.foldLeft(Try(h :: Nil)) {
          case (Success(acc), Success(elem)) => Success(elem :: acc)
          case (e : Failure[R], _) => e
          case (_, Failure(e)) => Failure(e)
        }
      case Failure(e) :: _  => Failure(e)
      case Nil => Try { Nil }
    }
  }
}

object trySeq {
  def withTry[R](run : Seq[R]): trySeq[R] = new trySeq(Try { run })
  def withSeq[R](run : Try[R]): trySeq[R] = new trySeq(run map (_ :: Nil))

  implicit def toTrySeqT[R](run : Try[Seq[R]]) = trySeq(run)
  implicit def fromTrySeqT[R](trySeqT : trySeq[R]) = trySeqT.run
} 

,然后您可以使用理解(只需导入您的库):

def foo : Try[Seq[String]] = Try { List("hello", "world") } 
def bar(s : String) : Try[String] = Try { s + "! " }

val x = for {
  item1  <- trySeq { foo }
  item2  <- trySeq { foo }
  result <- trySeq.withSeq { bar(item2) }
} yield item1 + result

println(x.run)

它适用于:

def foo() = Try { List("hello", throw new IllegalArgumentException()) } 
// x = Failure(java.lang.IllegalArgumentException)

Let's say you've got a bunch of methods:

def foo() : Try[Seq[String]]
def bar(s:String) : Try[String]

and you want to make a for-comprhension:

for {
  list <- foo
  item <- list
  result <- bar(item)
} yield result

of course this won't compile since Seq cannot be used with Try in this context.

Anyone has a nice solution how to write this clean without breaking it into separate two for's?

I've came across this syntax problem for the thirds time and thought that it's about time to ask about this.

解决方案

IMHO: Try and Seq is more than what you need to define a monad transformer:

Code for library:

case class trySeq[R](run : Try[Seq[R]]) {
  def map[B](f : R => B): trySeq[B] = trySeq(run map { _ map f })
  def flatMap[B](f : R => trySeq[B]): trySeq[B] = trySeq {
    run match {
      case Success(s) => sequence(s map f map { _.run }).map { _.flatten }
      case Failure(e) => Failure(e)
    }
  }

  def sequence[R](seq : Seq[Try[R]]): Try[Seq[R]] = {
    seq match {
      case Success(h) :: tail =>
        tail.foldLeft(Try(h :: Nil)) {
          case (Success(acc), Success(elem)) => Success(elem :: acc)
          case (e : Failure[R], _) => e
          case (_, Failure(e)) => Failure(e)
        }
      case Failure(e) :: _  => Failure(e)
      case Nil => Try { Nil }
    }
  }
}

object trySeq {
  def withTry[R](run : Seq[R]): trySeq[R] = new trySeq(Try { run })
  def withSeq[R](run : Try[R]): trySeq[R] = new trySeq(run map (_ :: Nil))

  implicit def toTrySeqT[R](run : Try[Seq[R]]) = trySeq(run)
  implicit def fromTrySeqT[R](trySeqT : trySeq[R]) = trySeqT.run
} 

and after you can use for-comrehension (just import your library):

def foo : Try[Seq[String]] = Try { List("hello", "world") } 
def bar(s : String) : Try[String] = Try { s + "! " }

val x = for {
  item1  <- trySeq { foo }
  item2  <- trySeq { foo }
  result <- trySeq.withSeq { bar(item2) }
} yield item1 + result

println(x.run)

and it works for:

def foo() = Try { List("hello", throw new IllegalArgumentException()) } 
// x = Failure(java.lang.IllegalArgumentException)

这篇关于在Scala中使用理解,尝试和排序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆