Я использую Either
большую часть времени, а не исключения. Обычно я использую исключения, как вы это делали, или каким-то другим способом, когда поток управления должен идти, возвращаясь в какую-то отдаленную точку, и в противном случае нет ничего разумного.Однако, когда исключения могут быть обработаны довольно локально, я буду вместо
def myMethod(...): Either[String,ValidatedInputForm] = {
...
if (!someHelper(...)) Left("Agree button not checked")
else Right(whateverForm)
}
, а затем, когда я называю этот метод, я могу
myMethod(blah).fold({ err =>
doSomething(err)
saneReturnValue
}, { form =>
foo(form)
form.usefulField
})
или матч на Left(err)
против Right(form)
или различных других вещей.
Если я не хочу, чтобы обработать ошибку прямо там, но вместо этого нужно обработать возвращаемое значение, я
myMethod(blah).right.map{ form =>
foo(form)
bar(form)
}
и я получаю Either
с сообщением об ошибке без изменений в качестве Left
, если это сообщение об ошибке, или с результатом { foo(form); bar(form) }
как Right
, если все было в порядке. Вы также можете связать свою обработку ошибок с помощью flatMap
, например. если вы хотите выполнить дополнительную проверку на так далеко правильные значения и отклонять некоторые из них, вы можете
myMethod(blah).right.flatMap{ form =>
if (!checkSomething(form)) Left("Something didn't check out.")
else Right(form)
}
Именно этот вид обработки, что делает использование Either
более удобным (и обычно более эффективным, если исключения являются обычными), чем исключения, поэтому я их и использую.
(На самом деле, в очень многих случаях я не забочусь почему что-то пошло не так, только что пошло не так, и в этом случае я просто использовать Option
.)
Да? Исключения предназначены именно для таких случаев! Так что часть приложения, которая взаимодействует с пользователем, может быть отделена от других частей системы. Если 'someHelper' - это некоторая локально полезная функция для преобразования данных, созданных с пользователем, она, конечно же, не должна знать о том, как связаться с пользователем, чтобы отобразить сообщение об ошибке. И модуль связи, вероятно, не должен знать о требованиях к данным 'someHelper', чтобы проверить его. – Ben
Будьте осторожны. Создание исключения заполняет трассировку стека, что может быть дорогостоящей операцией. Если вы пройдете этот маршрут, передайте исключение по имени, поэтому оно будет создано только в том случае, если вы его используете: 'def someMethod (e: => Exception)'. – leedm777
@Ben Я имел в виду, что исключения не предназначались для использования в качестве информации о состоянии приложения, а передача исключений (не метарование) и их окружающая логика выглядит очень похоже на это –