Некоторые языки утверждают, что у них нет «исключений времени выполнения», как явное преимущество перед другими языками, которые их имеют.
Я запутался в этом вопросе.
Насколько я знаю, исключение времени выполнения - это всего лишь инструмент:
- вы можете сообщать о «грязных» состояниях (выбрасывая неожиданные данные)
- добавив стек, вы можете указать на цепочку ошибок
- Вы можете различить беспорядок (например, возвращая пустое значение при неверном вводе) и небезопасное использование, которое требует внимания разработчика (например, выбрасывание исключения при неверном вводе)
- Вы можете добавить детали к вашей ошибке с сообщением об исключении, предоставляя дополнительную полезную информацию, помогающую отладке (теоретически)
С другой стороны, мне очень трудно отлаживать программное обеспечение, которое «проглатывает» исключения. Например
try {
myFailingCode();
} catch {
// no logs, no crashes, just a dirty state
}
Таким образом, вопрос заключается в следующем: в чем заключается сильное теоретическое преимущество отсутствия «исключений во время выполнения»?
пример
Нет ошибок времени выполнения на практике. Нет нуля Нет undefined не является функцией.
Ответы:
Исключения имеют крайне ограниченную семантику. Они должны обрабатываться именно там, где они выброшены, или в стеке прямых вызовов вверх, и программисту не нужно указывать во время компиляции, если вы забудете это сделать.
Сравните это с Elm, где ошибки кодируются как Results или Maybes , которые оба являются значениями . Это означает, что вы получите ошибку компилятора, если не обработаете ошибку. Вы можете хранить их в переменной или даже в коллекции, чтобы отложить их обработку до удобного времени. Вы можете создать функцию для обработки ошибок в зависимости от приложения, вместо того чтобы повторять очень похожие блоки try-catch повсеместно. Вы можете объединить их в вычисления, которые будут успешными, только если все его части будут успешными, и их не нужно будет объединять в один блок попытки. Вы не ограничены встроенным синтаксисом.
Это не что иное, как «проглатывание исключений». Это делает условия ошибок явными в системе типов и предоставляет гораздо более гибкую альтернативную семантику для их обработки.
Рассмотрим следующий пример. Вы можете вставить это в http://elm-lang.org/try, если хотите увидеть это в действии.
Обратите внимание, что
String.toInt
вcalculate
функции есть возможность сбоя. В Java это может вызвать исключение времени выполнения. Поскольку он читает пользовательский ввод, у него довольно хорошие шансы. Вместо этого, Элм заставляет меня разобраться с этим, возвращаяResult
, но заметьте, что мне не нужно сразу с этим разбираться. Я могу удвоить ввод и преобразовать его в строку, а затем проверить правильность ввода вgetDefault
функции. Это место гораздо лучше подходит для проверки, чем точка, где произошла ошибка, или вверх в стеке вызовов.То, как компилятор форсирует нашу руку, также намного тоньше, чем проверенные исключения Java. Вам нужно использовать очень специфическую функцию, например,
Result.withDefault
для извлечения нужного значения. Хотя технически вы можете злоупотреблять этим механизмом, особого смысла нет. Поскольку вы можете отложить принятие решения до тех пор, пока не узнаете хорошее сообщение по умолчанию / сообщение об ошибке, нет причин его не использовать.источник
That means you get a compiler error if you don't handle the error.
- Ну, это и есть причина Проверенных Исключений в Java, но мы все знаем, насколько хорошо это сработало.Maybe
,Either
и т.д. Elm выглядит он берет страницу из таких языков, как ML, OCaml или Haskell.x = some_func()
, мне не нужно ничего делать, если я не хочу проверить значениеx
, и в этом случае я могу проверить, есть ли у меня ошибка или «допустимое» значение; более того, попытка использовать одно вместо другого является статической ошибкой, поэтому я не могу этого сделать. Если типы Elm работают так же, как другие функциональные языки, я на самом деле могу делать такие вещи, как составление значений из разных функций, прежде чем я даже узнаю, являются ли они ошибками или нет! Это типично для языков FP.Чтобы понять это утверждение, мы сначала должны понять, что нам покупает статическая система типов. По сути, то, что дает нам статическая система типов, является гарантией: если программа проверяет тип, определенный класс поведения во время выполнения не может возникнуть.
Это звучит зловеще. Что ж, проверка типов похожа на проверку теорем. (На самом деле, согласно Каром-Говарду-Изоморфизму, это одно и то же.) Одна вещь, которая очень специфична в теоремах, это то, что когда вы доказываете теорему, вы доказываете в точности то, что говорит теорема, не более. (Вот, например, почему, когда кто-то говорит «Я доказал, что эта программа верна», вы всегда должны спрашивать «пожалуйста, определите« правильно »».) То же самое верно для систем типов. Когда мы говорим «программа является типобезопасной», мы имеем в виду не то, что возможная ошибка не возникает. Мы можем только сказать, что ошибки, которые система типов обещает нам предотвратить, не могут возникнуть.
Таким образом, программы могут иметь бесконечно много разных поведений во время выполнения. Из них бесконечно много полезных, но также бесконечно много «неправильных» (для разных определений «правильности»). Система статических типов позволяет нам доказать, что определенный конечный фиксированный набор этих бесконечно многих некорректных поведений во время выполнения не может возникнуть.
Разница между системами разных типов заключается в том, что, сколько и как сложно они могут возникнуть во время выполнения. Слабые системы типов, такие как Java, могут доказать только самые простые вещи. Например, Java может доказать, что метод, типизированный как возвращающий a,
String
не может вернуть aList
. Но, например, он не может доказать, что метод не вернется. Это также не может доказать, что метод не будет генерировать исключение. И он не может доказать, что он не вернет неправильноеString
- любойString
удовлетворит проверку типов. (И, конечно, дажеnull
удовлетворит его.) Есть даже очень простые вещи , которые Java не могут доказать, что именно поэтому у нас есть исключения , напримерArrayStoreException
,ClassCastException
или все любимец, тоNullPointerException
.Более мощные системы типов, такие как Agda, могут также доказать, что «вернет сумму двух аргументов» или «вернет отсортированную версию списка, переданного в качестве аргумента».
Теперь, что дизайнеры Elm подразумевают под утверждением, что у них нет исключений во время выполнения, так это то, что система типов Elm может доказать отсутствие (значительную часть) поведения во время выполнения, которое в других языках не может быть доказано, что оно не возникает и, следовательно, может привести к к ошибочному поведению во время выполнения (что в лучшем случае означает исключение, в худшем случае означает сбой, а в худшем случае все означает отсутствие сбоя, исключение и просто неверный результат).
Таким образом, они не говорят «мы не реализуем исключения». Они говорят, что «вещи, которые могли бы быть исключениями времени выполнения в типичных языках, с которыми обычные программисты, приходящие в Elm, имели бы опыт, попадают в систему типов». Конечно, кто-то из Идриса, Агды, Гуру, Эпиграма, Изабель / HOL, Coq или похожих языков увидит Элма довольно слабым по сравнению. Это утверждение больше предназначено для типичных программистов на Java, C♯, C ++, Objective-C, PHP, ECMAScript, Python, Ruby, Perl,….
источник
Elm может гарантировать отсутствие исключений времени выполнения по той же причине, что C может гарантировать отсутствие исключений времени выполнения: язык не поддерживает концепцию исключений.
В Elm есть способ оповещения об ошибках во время выполнения, но эта система не исключение, это «Результаты». Функция, которая может потерпеть неудачу, возвращает «Результат», который содержит либо обычное значение, либо ошибку. Вязы строго типизированы, так что это явно в системе типов. Если функция всегда возвращает целое число, она имеет тип
Int
. Но если он либо возвращает целое число, либо терпит неудачу, тип возвращаемого значенияResult Error Int
. (Строка является сообщением об ошибке.) Это заставляет вас явно обрабатывать оба случая на сайте вызова.Вот пример из введения (немного упрощенный):
Функция
toInt
может завершиться ошибкой, если входные данные не могут быть проанализированы, поэтому тип возвращаемого значения -Result String int
. Чтобы получить действительное целочисленное значение, вы должны «распаковать» с помощью сопоставления с образцом, что, в свою очередь, заставляет вас обрабатывать оба случая.Результаты и исключения в основном делают одно и то же, важным отличием являются «значения по умолчанию». Исключения всплывают и завершают работу программы по умолчанию, и вы должны явно их перехватить, если хотите обработать их. Результатом является другой путь - вы вынуждены обрабатывать их по умолчанию, поэтому вы должны явно передать их до самого верха, если вы хотите, чтобы они завершили программу. Легко увидеть, как такое поведение может привести к созданию более надежного кода.
источник
doSomeStuff(x: Int): Int
. Обычно вы ожидаете, что он вернет anInt
, но может ли он также выдать исключение? Не глядя на его исходный код, вы не можете знать. Напротив, язык B, который кодирует ошибки через типы, может иметь такую же функцию, объявленную так:doSomeStuff(x: Int): ErrorOrResultOfType<Int>
(в Elm этот тип фактически называетсяResult
). В отличие от первого случая, теперь сразу очевидно, может ли функция выйти из строя, и вы должны обращаться с ней явно.this is how you program in languages such as ML or Haskell
В Хаскеле, да; ML, нет. Роберт Харпер, основной участник Standard ML и исследователь языка программирования, считает исключения полезными . Типы ошибок могут мешать составлению функций в тех случаях, когда вы можете гарантировать, что ошибка не возникнет. Исключения также имеют различную производительность. Вы не платите за исключения, которые не генерируются, но вы платите за проверку значений ошибок каждый раз, а исключения являются естественным способом выражения возврата в некоторых алгоритмахВо-первых, обратите внимание, что ваш пример «проглатывания» исключений в целом является ужасной практикой и совершенно не связан с отсутствием исключений во время выполнения; когда вы об этом думаете, у вас была ошибка времени выполнения, но вы решили скрыть это и ничего не делать. Это часто приводит к ошибкам, которые трудно понять.
Этот вопрос может быть истолкован по-разному, но поскольку вы упомянули Elm в комментариях, контекст стал более понятным.
Элм, помимо прочего, является статически типизированным языком программирования. Одним из преимуществ систем такого типа является то, что многие классы ошибок (хотя и не все) обнаруживаются компилятором до фактического использования программы. Некоторые виды ошибок могут быть закодированы в типах (таких как Elm's
Result
иTask
), а не выбрасываться как исключения. Это то, что имеют в виду дизайнеры Elm: многие ошибки будут обнаружены во время компиляции, а не во время выполнения, и компилятор заставит вас разбираться с ними, а не игнорировать их и надеяться на лучшее. Понятно, почему это преимущество: лучше, чтобы программист узнал о проблеме раньше, чем пользователь.Обратите внимание, что когда вы не используете исключения, ошибки кодируются другими, менее удивительными способами. Из документации Вяза :
Дизайнеры Elm немного смелы в утверждении «никаких исключений во время выполнения» , хотя они квалифицируют его как «на практике». Вероятно, они означают «менее неожиданные ошибки, чем если бы вы кодировали в javascript».
источник
Result
иTask
которые очень похожи на более знакомыеEither
иFuture
с других языков. В отличие от исключений, значения этих типов могут быть объединены, и в какой-то момент вы должны явно обработать их: представляют ли они допустимое значение или ошибку? Я не читаю мысли, но это отсутствие удивления программиста, вероятно, то, что дизайнеры Elm имели в виду под «без исключений времени выполнения» :)Вяз утверждает:
Но вы спрашиваете об исключениях во время выполнения . Есть разница
В Elm ничего не дает неожиданного результата. Вы НЕ МОЖЕТЕ написать правильную программу в Elm, которая выдает ошибки во время выполнения. Таким образом, вам не нужны исключения.
Итак, вопрос должен быть:
Если вы можете написать код, который никогда не имеет ошибок во время выполнения, ваши программы никогда не будут аварийно завершаться.
источник