2015-07-14 4 views
20

В главе «Обработка ошибок без исключения» из книги «функциональное программирование в Scala», автор дает:Почему `scala.util.Try` не упоминается в главе« Обработка ошибок без исключений »книги« Функциональное программирование в Scala »?

  1. Проблема бросать исключения из тела функции
  2. Используйте Option если мы не» т забота о фактическом исключении
  3. Используйте Either, если мы заботимся о фактическом исключении

Но scala.util.Try не упоминается. С моей точки зрения, я думаю, что Try очень подходит, когда мы заботимся о фактическом исключении, почему это не упоминается? Есть ли какая-то причина, по которой я пропустил?

+1

Я предполагаю, что книга была написана до того, как Try был добавлен в стандартную библиотеку Scala. Попытка была добавлена ​​в версии 2.10. – marstran

+2

На стр. 58 и 61 они реализуют свою собственную функцию 'Try', используя свои определения' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''. _FP в Scala_ - это не книга о Scala, а о функциональном программировании (в Scala), поэтому упражнения переопределяют части существующего Scala API, чтобы изучить концепцию FP. –

+1

@marstran 'Try' существует с 2012 года, и книга была опубликована в этом году. Если «Try» был релевантным, и авторы хотели обсудить его, они бы это сделали. –

ответ

49

Я не являюсь автором функционального программирования в Scala, но я могу сделать несколько догадок о том, почему они не упоминают Try.

Некоторые люди не любят стандартную библиотеку Try, потому что они требуют it violates the functor composition law. Я лично считаю, что эта позиция глупа, по причинам, которые Джош Суэрет упоминает в комментариях SI-6284, но в дебатах подчеркивается важный аспект дизайна Try.

Trymap и flatMap явно предназначены для работы с функциями, которые могут вызывать исключения. Люди из школы мысли FPiS (включая меня) склонны предлагать обертывать такие функции (если вам абсолютно необходимо иметь дело с ними вообще) в безопасных версиях на низком уровне в вашей программе, а затем выставлять API, который никогда не будет бросать (нефатальные) исключения.

В том числе Try в вашем API путает слои в этой модели - вы гарантируете, что ваши методы API не будут генерировать исключения, но тогда вы передаете людям тип, который предназначен для использования с функциями, которые генерируют исключения ,

Это только жалоба на дизайн и реализацию стандартной библиотеки Try. Достаточно легко представить себе версию Try с различной семантикой, где методы map и flatMap не исключают исключения, и все равно будут веские причины избегать этой «улучшенной» версии Try, когда это возможно.

Одна из этих причин заключается в том, что использование Either[MyExceptionType, A] вместо Try[A] позволяет получить больше пробега из проверки работоспособности компилятора. Предположим, я использую следующую простую ADT ошибок в моем приложении:

sealed class FooAppError(message: String) extends Exception(message) 

case class InvalidInput(message: String) extends FooAppError(message) 
case class MissingField(fieldName: String) extends FooAppError(
    s"$fieldName field is missing" 
) 

Теперь я пытаюсь решить, является ли метод, который может только неудачу в одном из этих двух способов должен возвращать Either[FooAppError, A] или Try[A]. Выбор Try[A] означает, что мы отбрасываем информацию, которая потенциально полезна как для пользователей, так и для компилятора. Предположим, что я пишу метод, как это:

def doSomething(result: Either[FooAppError, String]) = result match { 
    case Right(x) => x 
    case Left(MissingField(_)) => "bad" 
} 

я получить хорошее предупреждение во время компиляции, говоря мне, что матч не является исчерпывающим. Если я добавлю кейс для отсутствующей ошибки, предупреждение исчезнет.

Если бы я использовал Try[String] вместо этого, я бы также получить проверку exhaustivity, но единственный способ избавиться от предупреждения будет иметь улов-все дело, это просто невозможно перечислить все Throwable с в соответствие шаблону.

Иногда мы практически не можем ограничить виды действий, которые могут привести к поломке нашего собственного типа отказа (например, FooAppError выше), и в этих случаях мы всегда можем использовать Either[Throwable, A]. Например, Scalaz's Task является, по существу, оберткой для Future[Throwable \/ A]. Разница заключается в том, что Either (или) поддерживает этот тип подписи, в то время как Tryтребует. И это не всегда то, что вы хотите, по причинам, как полезная проверка полноты.

+0

В качестве примечания см. Мой ответ [здесь] (http://stackoverflow.com/a/21665747/334519) для дополнительного обсуждения. –

+3

Хороший ответ, хотя, я думаю, вы серьезно недооцениваете значение 'Try' в укрощении непослушного внешнего кода. Обертывание вещей требует времени. «Попробуйте», это потрясающе, когда все может разрастись разными способами, но все равно полезно получить непрерывный ответ, если у вас его есть. –

+1

@RexKerr Согласен! Во время моей повседневной работы мы сильно полагаемся на «Try» (наш собственный, но он по существу тот же, что и у стандартной библиотеки), и если альтернатива - это исключения везде «Try» - это фантастика. Наверное, я мог бы немного пояснить, что я отвечаю на вопрос: «Почему некоторым людям может не нравиться« Попробовать »?», А не «я должен избегать« Попробовать »?». –