Разница между Try и Try Catch - PullRequest
0 голосов
/ 06 мая 2018

Я понимаю, что есть разница между Try / Success / Failure и Try Catch. В официальном документе также есть хороший пример:

import scala.io.StdIn
import scala.util.{Try, Success, Failure}

def divide: Try[Int] = {
  val dividend = Try(StdIn.readLine("Enter an Int that you'd like to divide:\n").toInt)
  val divisor = Try(StdIn.readLine("Enter an Int that you'd like to divide by:\n").toInt)
  val problem = dividend.flatMap(x => divisor.map(y => x/y))
  problem match {
    case Success(v) =>
      println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
      Success(v)
    case Failure(e) =>
      println("You must've divided by zero or entered something that's not an Int. Try again!")
      println("Info from the exception: " + e.getMessage)
      divide <------------ Here!
  }
}

Однако мне трудно понять

Важным свойством Try, показанным в приведенном выше примере, является его способность на конвейер или цепочку операций, перехватывая исключения по пути. Комбинаторы flatMap и map в приведенном выше примере, по сути, каждый выдать либо их успешно завершенное значение, завернутые в Тип успеха для дальнейшего использования следующим комбинатором в цепочке, или исключение, заключенное в тип Failure, обычно быть просто переданным по цепочке. Комбинаторы, такие как восстановление и recoveryWith предназначены для обеспечения некоторого типа поведения по умолчанию в случай неудачи.

Есть ли примеры, чтобы проиллюстрировать, как облегчить "конвейер, цепочку, операции ...."? Какие-нибудь примеры, чтобы проиллюстрировать, почему у нас нет одинаковых преимуществ try-catch?

Кроме того, почему мы возвращаем divide в Failure? Иначе у нас будет type mismatch? Это единственная причина?

Ответы [ 3 ]

0 голосов
/ 06 мая 2018

Try[A] представляет вычисление, которое, если оно выполнено, является значением типа A, в противном случае что-то пошло не так и будет Throwable. Он поставляется в двух вариантах: при успешном вычислении это значение типа A, заключенное в экземпляр Success[A]. В случае сбоя это экземпляр Failure[A], который включает исключение Throwable.

Одной из характеристик Try является то, что он позволяет очень умно использовать функции высокого порядка (например, Option, Either и другие монады) и цепные операции.

Допустим, у вас есть эта маленькая программа:

scala> def divide(x: Int, y: Int): Try[Int] =
         if (y == 0) Try(throw new Exception("Error: division by zero!"))
         else Try(x / y)
divide: (x: Int, y: Int)scala.util.Try[Int]

Вы получаете

scala> divide(3,2)
res30: scala.util.Try[Int] = Success(1)

и

scala> divide(3,0)
res31: scala.util.Try[Int] = Failure(java.lang.Exception: Error: division by zero!)

Таким образом, вы можете использовать возможность функций высокого порядка для элегантного распознавания сбоев:

scala> divide(3,0).getOrElse(0)
res32: Int = 0

Это всего лишь пример, но подумайте о том, пытались ли вы выполнить более сложное вычисление, которое могло бы потерпеть неудачу, и вам пришлось зафиксировать этот результат и действовать в результате.

Почему бы вам не использовать try catchs в Scala? Это не потому, что вы не сможете этого сделать, а потому, что это не очень функционально. Если вам нужно обработать исключение, которое возникает в другом потоке (скажем, актером), вы не можете перехватить это исключение, возможно, вы захотите передать сообщение вашему текущему основному потоку о том, что вычисление не удалось, и вы выясним, как справиться с этим.

Кроме того, почему мы возвращаем разделение в случае отказа? Иначе у нас будет несоответствие типов? Это единственная причина?

Возвращает divide в Failure, потому что позволяет перезапустить программу, если она не удалась, но это просто решение, принятое программистом. Вы могли бы просто возвратить Failure обертывание какого-то сообщения об ошибке.

0 голосов
/ 06 мая 2018

Try[A]

Это алгебраический тип данных (ADT), состоящий из 2 случаев: Success[A] и Failure[A]. Эта алгебраическая структура определяет множество операций, таких как map, flatMap и другие. Тот факт, что Try имеет map и flatMap плюс конструктор от A до Try[A], делает эту структуру монадой.

Монады - это абстракции, которые позволяют нам функционально выражать последовательности вычислений. Давайте посмотрим пример

def tryDivide(v1: Double, v2: Double): Try[Double] = Try(v1 / v2)

val tryDivisions = tryDivide(1.0, 2.0).flatMap {
   res => tryDivide(res, 2)
}.flatMap {
   res => tryDivide(res, 0)
}

в результате этого кода мы получим Failure[A], потому что последняя операция делится на ноль. Тот факт, что Try является монадой, позволяет нам переписать это, используя для понимания, как это

for {
   v1 <- tryDivide(1.0, 2.0)
   v2 <- tryDivide(v1, 2)
   res <- tryDivide(v2, 0)
} yield res

try

С другой стороны, try - это просто синтаксис языка, который позволяет вам ловить исключения, которые могут быть выброшены из вашего кода, но это не алгебраическая структура с методами, которые вы можете выполнять.

0 голосов
/ 06 мая 2018

Во-первых, некоторые сведения о flatMap (): эта функция во многих отношениях ссылается на хорошо известную концепцию монадического оператора> = (bind), если вы хотите узнать об этом более подробно, я настоятельно рекомендую прочитать книгу «Изучение Ты Хаскель за Великое Добро!

Но, говоря проще, попробуйте сделать следующее:

someTry.flatMap(//doSomething1).flatMap(//doSomething2)

, а затем выполнить какое-либо сопоставление, чтобы получить значение или получить ошибку из первой или второй функции (обратите внимание, что любая flatMap из error не будет вычислена и ошибка останется прежней), но со старым способом вы будете делать что-то подобное это:

try{
    //doSomething()
} catch {
    ...
}
try {
    //domSomething2()
} catch {
    ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...