2011-12-22 3 views
1

Я пишу веб-приложение, где исключения используются для обработки ошибок. Часто я считаю себя писать хелперы, как это:Исключение Scala как шаблон шаблона аргумента функции

def someHelper(...) : Boolean {...} 

, а затем использовать его как это:

if (!someHelper(...)){ 
    throw new SomeException() 
} 

Эти исключения представляют собой такие вещи, как неправильные параметры, и при обращении с отправкой сообщения полезной ошибке в пользователь, например

try { 
    ... 
} catch { 
    case e: SomeException => "Bad user!" 
} 

Это разумный подход? И как я могу передать исключение в вспомогательную функцию и выбросить ее? У меня возникла проблема с построением типа для такой функции.

ответ

6

Я использую 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.)

1

Там нет ничего особенного о передаче экземпляра исключения к каким-либо способом:

def someMethod(e: SomeException) { 
    throw e 
} 
someMethod(new SomeException) 

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

+0

Да? Исключения предназначены именно для таких случаев! Так что часть приложения, которая взаимодействует с пользователем, может быть отделена от других частей системы. Если 'someHelper' - это некоторая локально полезная функция для преобразования данных, созданных с пользователем, она, конечно же, не должна знать о том, как связаться с пользователем, чтобы отобразить сообщение об ошибке. И модуль связи, вероятно, не должен знать о требованиях к данным 'someHelper', чтобы проверить его. – Ben

+0

Будьте осторожны. Создание исключения заполняет трассировку стека, что может быть дорогостоящей операцией. Если вы пройдете этот маршрут, передайте исключение по имени, поэтому оно будет создано только в том случае, если вы его используете: 'def someMethod (e: => Exception)'. – leedm777

+0

@Ben Я имел в виду, что исключения не предназначались для использования в качестве информации о состоянии приложения, а передача исключений (не метарование) и их окружающая логика выглядит очень похоже на это –

1

Два наиболее распространенных способа приблизиться к тому, что вы пытаетесь сделать, это либо создать хелпер, либо создать исключение, либо точно, что вы делаете: иметь код вызова, проверить результаты и бросить значимое исключение, если необходимо.

Я никогда не видел библиотеку, в которой вы проходите, за исключением того, что вы ожидаете от помощника. Как я уже сказал, on another answer, есть удивительно значительные затраты на простое создание исключения, и если вы следовали этому шаблону во всем коде, вы могли бы увидеть общую проблему с производительностью. Это можно было бы смягчить с помощью by-name parameters, но если вы просто забудете поставить => в несколько ключевых функций, у вас есть проблема с производительностью, которую сложно отследить.

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

def helperA(...) { if (stuff) throw new InvalidStuff() } 

def helperB(..., onError: => Exception) { if (stuff) throw onError } 

Я бы выбрал A каждый раз.

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

В заключение обратите внимание, что именование очень важно в таких ситуациях. Если ваш маршрут возврата-кода-помощника, ваши помощники должны иметь имена вопросов, например isValid. Если у вас есть помощники исключения, они должны иметь имена действий, например validate. Может быть, даже уделять этому внимание, например, validate_!.

0

В качестве альтернативного подхода вы можете проверить scalaz Валидаторы, которые дают большую гибкость для такого рода случаев (например, я должен сбой при ошибке, накапливать ошибки и сообщать в конце или игнорировать их полностью?). A few examples может помочь вам решить, подходит ли это для вас.

Если вам трудно найти способ войти в библиотеку, this answer дает некоторые указатели на некоторые вводные материалы; или проверить.

Смежные вопросы