【问题标题】:Scala Yeild returning Try[Either[]] rather then EitherScala Yield 返回 Try[Either[]] 而不是 Either
【发布时间】:2020-06-24 07:40:52
【问题描述】:

我正在尝试对 scala 基本操作进行一些动手操作,但在以下示例代码中卡住了

  def insuranceRateQuote(a: Int, tickets:Int) : Either[Exception, Double] = {
    // ... something
    Right(Double)
  }

  def parseInsuranceQuoteFromWebForm(age: String, numOfTickets: String)  : Either[Exception, Double]= {
    try{
      val a = Try(age.toInt)
      val tickets = Try(numOfTickets.toInt)

      for{
        aa <- a
        t <- tickets
      } yield insuranceRateQuote(aa,t)        // ERROR HERE
    } catch {
      case _ => Left(new Exception)}
  }

我得到的错误是它说found Try[Either[Exception,Double]]

我不明白为什么它是 Try of Either 下的包装器

PS - 这一定不是在 scala 中做的完美方式,所以请随意发布您的示例代码 :)

【问题讨论】:

  • Try 包裹在 try 中是很奇怪的。

标签: scala try-catch for-comprehension either


【解决方案1】:

要理解的关键是for-comprehensions 可能会改变包装器内部的内容,但不会改变包装器本身。原因是因为对map/flatMap 的理解去糖调用了在链的第一步中确定的包装器。例如考虑以下 sn-p

val result: Try[Int] = Try(41).map(v => v + 1)
// result: scala.util.Try[Int] = Success(42)

请注意我们如何将 Try 包装器中的值从 41 转换为 42 但包装器保持不变。或者,我们可以使用 for-comprehension 来表达同样的事情

val result: Try[Int] = for { v <- Try(41) } yield v + 1
// result: scala.util.Try[Int] = Success(42)

注意效果是如何完全一样的。现在考虑以下链接多个步骤的理解

val result: Try[Int] =
  for {
    a <- Try(41)   // first step determines the wrapper for all the other steps
    b <- Try(1)
  } yield a + b
// result: scala.util.Try[Int] = Success(42)

这扩展为

val result: Try[Int] =
  Try(41).flatMap { (a: Int) =>
    Try(1).map { (b: Int) => a + b }
  }
// result: scala.util.Try[Int] = Success(42)

我们再次看到结果是相同的,即在包装器内部转换的值但包装器保持未转换。

最后考虑

val result: Try[Either[Exception, Int]] =
  for {
    a <- Try(41)       // first step still determines the top-level wrapper
    b <- Try(1)
  } yield Right(a + b) // here we wrap inside `Either`
// result: scala.util.Try[Either[Exception,Int]] = Success(Right(42))

原理保持不变 - 我们确实将 a + b 包裹在 Either 内,但这不会影响仍然是 Try 的顶级外包装。

【讨论】:

  • 我更新了我的函数定义如下def parseInsuranceQuoteFromWebForm(age: String, numOfTickets: String) : Try[Either[Exception, Double]] 如果这看起来不错,请告诉我,或者我可以进一步修改一些 scala 的东西。谢谢!
  • @Ani 最好的方法是将 Either 转换为 Tryviceversa,这样你最终会得到只是一种包装。特别是因为 EitherTry 很难使用,而且通常没有意义。
【解决方案2】:

Mario Galic 的回答已经解释了您的代码的问题,但我会以不同的方式解决它。

两点:

  1. Either[Exception, A](或者更确切地说,Either[Throwable, A])有点等价于Try[A],其中Left扮​​演Failure的角色,Right扮演Success的角色。

  2. 外部try/catch 没有用处,因为应该通过在Try 中工作来捕获异常。

所以你可能想要类似的东西

def insuranceRateQuote(a: Int, tickets:Int) : Try[Double] = {
  // ... something
  Success(someDouble)
}

def parseInsuranceQuoteFromWebForm(age: String, numOfTickets: String):  Try[Double] = {
  val a = Try(age.toInt)
  val tickets = Try(numOfTickets.toInt)

  for{
    aa <- a
    t <- tickets
    q <- insuranceRateQuote(aa,t)
  } yield q
}

有点遗憾,如果你弄清楚了理解的作用,这会产生无用的map(q =&gt; q),所以你可以更直接地写成

a.flatMap(aa => tickets.flatMap(t => insuranceRateQuote(aa,t)))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-26
    • 1970-01-01
    • 1970-01-01
    • 2020-09-08
    • 2019-07-06
    • 2019-02-09
    • 2015-01-05
    • 1970-01-01
    相关资源
    最近更新 更多