Является ли использование исключений плохой практикой в scala?
Я видел много раз фрагменты кода scala, используя Option (для простых значений) или Либо [List [Error], T] для обработки ошибок.
это дает место для кода вроде этого
def createApplicationToken(accessToken: AccessToken): Either[List[Error], ApplicationToken] = {
// go to social info provider and fetch information
retrieveProviderInfo(accessToken).fold(
errors => Left(errors),
info => {
// try to find user using the info from the provider
// if it not there, create user
User.findOrCreateFromProviderInfo(info).fold(
errors => Left(errors),
user => {
// try to create a fresh token and save it to the user
user.refreshApplicationToken.fold(
errors => Left(errors),
user => Right(user.token)
)
}
)
}
)
Что создает не очень приятное вложение кода, заставляет вас справляться с ошибками на каждом шаге, а также заставляет вас возвращать все ваши функции Aither [...]
Итак, я хотел бы знать, если
-
использование исключений не рекомендуется в scala (или вообще функциональном программировании)
-
есть какие-либо недостатки в их использовании (относительно неизменяемости или кода concurrency)
-
исключения как-то противоречат принципам или функциональному программированию
-
вы можете придумать лучший способ кодирования данного примера
-
Можно было бы избежать вложенности, выйдя из функции, как только ошибка будет найдена с помощью оператора return, но использование return также не рекомендуется в scala...
Ответы
Ответ 1
В следующей версии используется тот факт, что правая проекция Either
является монадой и в точности эквивалентна вашему коду:
def createApplicationToken(accessToken: AccessToken) = for {
info <- retrieveProviderInfo(accessToken).right
user <- User.findOrCreateFromProviderInfo(info).right
refr <- user.refreshApplicationToken.right
} yield refr.token
И гораздо лучше продемонстрировать преимущества Either
.
В более общем плане правила такие же, как в Java: используйте исключения для исключительных ситуаций. Вы просто можете обнаружить, что вы изменили свое определение исключительного момента, когда работаете в этом стиле, например, неверный ввод пользователя не является действительно исключительным, тайм-аутный сетевой запрос не является действительно исключительным и т.д.
Ответ 2
Как сказал om-nom-nom, я задал аналогичный вопрос:
Выбрасывание исключений в Scala, что такое "официальное правило"
Но это не единственный, который я спросил, который может вас заинтересовать, потому что я использовал код с большим количеством шаблонов и много уровней отступов из-за соответствия шаблонов и т.д.
-
Вы можете проверить эти ссылки:
Обработка сбоев с помощью Either → Где находится stacktrace?
-
Также можно относиться к обработке ошибок, которые могут вас заинтересовать:
Проверка параметров метода в Scala, для понимания и монад
В котором Трэвис Браун дал более подробный ответ об использовании аппликативных функторов и Scalaz для выполнения отказов (первая ошибка блокирует процесс) или собирает все ошибки набора операций
Тот же вопрос: Обработка отказоустойчивых сбоев, когда тип возврата - опция [Ошибка]
-
И вы можете проверить эту ссылку, которая использует параметры by-name, выполнить последовательность операций. Это может быть хорошей альтернативой использованию правильных проекций для понимания, но вы не можете создавать промежуточные результаты:(
Проверка с последовательностью параметров имени в Scala?
Я не знаю, может ли он использоваться с вашим примером кода, но я не думаю, что в этом случае (предполагая, что ваш refreshApplicationToken свободен от побочных эффектов и возвращает вновь созданный неизменяемый пользовательский экземпляр вместо изменения переменной токена)
Ответ 3
Ответ зависит от идеального и практического. В идеале избегайте использования исключений. Практически, вы не можете жить без них.
Scala кажется предпочтительным для однострочных линий, а вдоль этих строк v2.10 имеет новую монаду Try:
import scala.util.Try
def percentCompleted( total:Int, done:Int ): Int = Try (done * 100 / total) getOrElse 100
percentCompleted( 0, 10 ) // Catches divide-by-zero and returns 100% instead