Я не являюсь автором функционального программирования в Scala, но я могу сделать несколько догадок о том, почему они не упоминают Try
.
Некоторые люди не любят стандартную библиотеку Try
, потому что они требуют it violates the functor composition law. Я лично считаю, что эта позиция глупа, по причинам, которые Джош Суэрет упоминает в комментариях SI-6284, но в дебатах подчеркивается важный аспект дизайна Try
.
Try
map
и 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
требует. И это не всегда то, что вы хотите, по причинам, как полезная проверка полноты.
Я предполагаю, что книга была написана до того, как Try был добавлен в стандартную библиотеку Scala. Попытка была добавлена в версии 2.10. – marstran
На стр. 58 и 61 они реализуют свою собственную функцию 'Try', используя свои определения' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''. _FP в Scala_ - это не книга о Scala, а о функциональном программировании (в Scala), поэтому упражнения переопределяют части существующего Scala API, чтобы изучить концепцию FP. –
@marstran 'Try' существует с 2012 года, и книга была опубликована в этом году. Если «Try» был релевантным, и авторы хотели обсудить его, они бы это сделали. –