Давайте лучше не вводить искусственных ограничений… Если говорить о том, что лучше, то равняться надо на лучшие подходы, а не только на самые популярные в данный момент (C#, Java) и самые старинные (C, Go).
В Go не хватает нативной поддержки варианта, когда это нештатное поведение. Достаточно было бы сделать аля сопоставление с образцом file, nil = os.Open("file.txt")
Но так как этого нет, то происходит навязывание рассматривать абсолютно все ошибки, как штатное поведение. Это порождает совершенно лишний boilerplate-код. И я не понимаю, о чём тут спорить… Попробуйте хоть один ЯП, который не принуждает Вас выбирать что-то одно (код возврата vs исключение) в 100% случаев и сами всё поймёте.
Я не пойму, у Вас на эту тему какая-то домашняя заготовка что-ли?
Я Вам про Let It Crash, а Вы мне про "кто эту ошибку словит", "try и скобочки"… Не надо исключения перехватывать, они на то и исключения, чтобы уронить подпроцесс и дать ему перезапуститься с чистого листа.
Непонятно в каком месте тут жертвы в читабельности, производительности и понятности? Кода обработки исключений нет вообще, читаемость и понятность у happy path самая шикарная из всех возможных. Если у Вас исключения происходят настолько часто, что затрагивают производительность, то либо Вы их используете для штатных ситуаций (control flow), либо у Вас общесистемные проблемы, возможно SSD на сервере пора менять или что-то в этом роде.
А мне понравилась статья. На мой взгляд она не столько про конкретный код, сколько про то, что не нужно увлекаться оверинженерингом. Задача то на самом деле была нетиповая и получившееся решение выглядит весьма элегантно. Код, конечно, можно ещё подшлифовать, но куда ж без этого.
Плюс я узнал про существование Vivus и Writage. Кстати, интересно, насколько хорошо Writage справляется со своей задачей в плане истории изменений? Т.е. насколько сильно результат отличается от ручного редактирования Markdown, если отличается?
Вот тут я вижу в ваших словах непримиримое противоречие.
Не мудрено увидеть противоречия если выдрать 2 предложения из разных контекстов ))) Я нигде не писал, что ошибка при открытии файла не может быть штатным поведением, даже наоборот я писал чуть выше, что она может быть и тем и тем. Вы попросили пример когда она является исключительной ситуацией. А сами привели пример, когда она является штатным поведением. Ну как бы и что дальше?
функция для открытия файла — которая занимается как раз всей низкоуровневой работой, она может встретить ошибочную ситуацию — файла нет, или доступ запрещён или ещё что — и вот что она должна сделать?
Очевидно же, она должна позволять работать в обоих режимах. Если я ожидаю ошибку, то вызову вариант возвращающий статус, если нет — то вариант, кидающий исключение. Я начинаю понимать, что Вы смотрите на этот вопрос с позиции, что надо выбрать что-то одно, но нет! Необходимости выбирать что-то одно для всех случаев тут нет.
Ну, например, в Elixir. Там по соглашению, имена методов, которые могут вызвать исключение заканчиваются на !. Допустим, File.open и File.open!. Весьма удобно для разделения штатных и исключительных ситуаций на уровне кода.
P.S. Обратите внимание, что я не говорю, что использовать повсюду исключения — это хорошо, наоборот, это так же плохо как повсюду проверять код возврата :-)
А главная проблема в том, что нет изоляции на уровне пакетов. Если в чужом коде возникла ошибка, она имеет все шансы добраться до вашего основного приложения и даже уронить его.
Timeout в Java и C# являются исключительной ситуацией, но в большинстве случаев это не Let It Crash, а стандартное поведение того же сетевого кода, которое можно или игнорировать, или использовать для попытки переподключения.
Так Let It Crash как раз и организует Вам переподключение. Это дефолтное поведение в рамках этой методологии: убить корутину и перезапустить её с чистого листа, чтобы ещё раз попробовать.
Речь здесь о том, что в языках с исключениями нет разделения на исключительные ситуации, когда нужно только падать, и просто ошибками, которые можно обработать и без всяких последствий продолжить работу, либо чуть подкорректировать ее.
Ну это скорее от стандартной библиотеки ЯП зависит, а не от факта поддержки исключений. Ну и даже если stdlib подкачала, есть же варианты… Например, перед открытием файла можно проверить, что он существует. Да это не гарантирует, что он откроется, но всё же если он существовал на момент проверки, но не открылся — это уже будет ближе к исключительной ситуации.
чтобы не терять время, для начала пример «исключительной ситуации» (в вашем понимании) в приведенном же вами примере — открытие и чтение из файла.
Например, пользователи загружают на сервер файлы для какого-то преобразования… для этого преобразования мы используем внешнюю программу. После её успешного завершения, мы читаем результат из файла, путь к которому передан программе. Если при этом файл открыть не удаётся, то это исключительная ситуация.
как «штатное поведение», так и «исключительная ситуация» — это определяется вашей бизнес-логикой.
Исключительные ситуации на то и исключительные, что они не определяются бизнес-логикой. Бизнес-логика даже не догадывается о том, что они вообще возможны.
Как нескромно, о себе во множественном числе ))) Впрочем, давайте не будем опытом мериться.
Лучше приведите хоть 1 пример исключительной ситуации, когда проверка статуса возврата хоть чем-то лучше Let It Crash. Пока Вы приводите примеры штатного поведения… Применять к ним термины "ошибка" и "исключение", на мой взгляд, в принципе некорректно. В случае же исключительной ситуации единственное возможное поведение — это как можно быстрее привести в систему в рабочее состояние после сбоя.
Я знаю, просто divan0 смешивает запасной вариант и исключение на уровне понимания.
В зависимости от контекста одна и та же вызов функции может приводить и к тому и к другому, но концептуально это разные вещи. Поэтому во многих языках библиотечные функции имеют 2 формы: одна возвращает статус и результат, а вторая — только результат, а в случае ошибки кидает исключение.
В Go для второго случая придётся в куче мест писать if err != nil { panic(err) }
А если вам нужно при ошибке открытия файла, попытаться открыть другой файл?
То это не исключение, т.к. у нас есть другой файл.
А если при ошибке чтения вам нужно использовать дефолтное значение?
То это не исключение, т.к. у нас есть дефолтное значение.
А если при записи, вы должны залоггировать ошибку, а при закрытии — проигнорировать?
То это прописанная бизнес-логика.
Всё, что Вы приводите в качестве примера, строго говоря, не имеет отношения к исключительным ситуациям. Т.к. исключение — это неожидаемое поведение, обработка которого не описана в требованиях.
«Отсутствующий код для проверки ошибок» — это точно не лучший вид кода, вот гарантирую.
См. мой комментарий чуть выше. Разработчики отказоустойчивых систем с Вами категорически не согласны :-)
Самое интересное, что для большинства практических применений лучшая обработка ошибок будет в подходе "Let It Crash". Проверять коды возврата, ловить исключения, — это всё равно путь в никуда при обработке настоящих исключительных ситуаций (таких, которые не должны произойти), в нетривиальной программе вы по-любому пропустите обработку какого-нибудь кода возврата, при этом мало того, что код будет захламлён, так ещё и варианта восстановления именно на этот случай не будет.
Разбаловала Вас дешёвая ОЗУ, я помню лет 8 назад сталкивался по работе с аналогичной задачей… И свелась она к тому, чтобы построчно читать XML и как только нужный узел был прочитан, он сразу отправлялся в обработку, освобождая память для следующего. В итоге программа не выходила за 100 Mb по памяти (точное значение уже не помню), несмотря на 2 Gb обрабатываемого XML.
Всё очень относительно… сейчас плата, умещающаяся на ладони, может быть в разы мощнее персонального компьютера 10-летней давности.
Тут скорее надо определиться с восприятием, что такое эмбед? Это ограниченные ресурсы или ограниченный размер… Если второе, то ресурсов хватит практически на любой современный ЯП.
Вот интересно, кстати, почему? Очевидно, что сейчас в мире уже гораздо больше embedded систем, чем программистов, способных написать для них безопасный код на C.
По факту хвостовая рекурсия раскрывается в тот же ассемблерный код, что и обычный цикл.
А в комментарии, видимо, имеется в виду проблема, когда стек заканчивается. Рекурсия, использующая стек, обычно применяется в императивных языках, т.к. там может не быть tail-call optimization. Ну либо в крайне редком подмножестве алгоритмов, где без древовидной рекурсии вообще никак не обойтись.
Индекс TIOBE строится на основе количества запросов к поисковикам. Т.е., строго говоря, он не имеет корреляции с распространённостью языка или с количеством кода на нём написанном.
Давайте лучше не вводить искусственных ограничений… Если говорить о том, что лучше, то равняться надо на лучшие подходы, а не только на самые популярные в данный момент (C#, Java) и самые старинные (C, Go).
В Go не хватает нативной поддержки варианта, когда это нештатное поведение. Достаточно было бы сделать аля сопоставление с образцом
file, nil = os.Open("file.txt")Но так как этого нет, то происходит навязывание рассматривать абсолютно все ошибки, как штатное поведение. Это порождает совершенно лишний boilerplate-код. И я не понимаю, о чём тут спорить… Попробуйте хоть один ЯП, который не принуждает Вас выбирать что-то одно (код возврата vs исключение) в 100% случаев и сами всё поймёте.
Я не пойму, у Вас на эту тему какая-то домашняя заготовка что-ли?
Я Вам про Let It Crash, а Вы мне про "кто эту ошибку словит", "try и скобочки"… Не надо исключения перехватывать, они на то и исключения, чтобы уронить подпроцесс и дать ему перезапуститься с чистого листа.
Непонятно в каком месте тут жертвы в читабельности, производительности и понятности? Кода обработки исключений нет вообще, читаемость и понятность у happy path самая шикарная из всех возможных. Если у Вас исключения происходят настолько часто, что затрагивают производительность, то либо Вы их используете для штатных ситуаций (control flow), либо у Вас общесистемные проблемы, возможно SSD на сервере пора менять или что-то в этом роде.
А мне понравилась статья. На мой взгляд она не столько про конкретный код, сколько про то, что не нужно увлекаться оверинженерингом. Задача то на самом деле была нетиповая и получившееся решение выглядит весьма элегантно. Код, конечно, можно ещё подшлифовать, но куда ж без этого.
Плюс я узнал про существование Vivus и Writage. Кстати, интересно, насколько хорошо Writage справляется со своей задачей в плане истории изменений? Т.е. насколько сильно результат отличается от ручного редактирования Markdown, если отличается?
Не мудрено увидеть противоречия если выдрать 2 предложения из разных контекстов ))) Я нигде не писал, что ошибка при открытии файла не может быть штатным поведением, даже наоборот я писал чуть выше, что она может быть и тем и тем. Вы попросили пример когда она является исключительной ситуацией. А сами привели пример, когда она является штатным поведением. Ну как бы и что дальше?
Очевидно же, она должна позволять работать в обоих режимах. Если я ожидаю ошибку, то вызову вариант возвращающий статус, если нет — то вариант, кидающий исключение. Я начинаю понимать, что Вы смотрите на этот вопрос с позиции, что надо выбрать что-то одно, но нет! Необходимости выбирать что-то одно для всех случаев тут нет.
Ну, например, в Elixir. Там по соглашению, имена методов, которые могут вызвать исключение заканчиваются на
!. Допустим, File.open и File.open!. Весьма удобно для разделения штатных и исключительных ситуаций на уровне кода.P.S. Обратите внимание, что я не говорю, что использовать повсюду исключения — это хорошо, наоборот, это так же плохо как повсюду проверять код возврата :-)
А главная проблема в том, что нет изоляции на уровне пакетов. Если в чужом коде возникла ошибка, она имеет все шансы добраться до вашего основного приложения и даже уронить его.
Так Let It Crash как раз и организует Вам переподключение. Это дефолтное поведение в рамках этой методологии: убить корутину и перезапустить её с чистого листа, чтобы ещё раз попробовать.
Ну это скорее от стандартной библиотеки ЯП зависит, а не от факта поддержки исключений. Ну и даже если stdlib подкачала, есть же варианты… Например, перед открытием файла можно проверить, что он существует. Да это не гарантирует, что он откроется, но всё же если он существовал на момент проверки, но не открылся — это уже будет ближе к исключительной ситуации.
Например, пользователи загружают на сервер файлы для какого-то преобразования… для этого преобразования мы используем внешнюю программу. После её успешного завершения, мы читаем результат из файла, путь к которому передан программе. Если при этом файл открыть не удаётся, то это исключительная ситуация.
Исключительные ситуации на то и исключительные, что они не определяются бизнес-логикой. Бизнес-логика даже не догадывается о том, что они вообще возможны.
Как нескромно, о себе во множественном числе ))) Впрочем, давайте не будем опытом мериться.
Лучше приведите хоть 1 пример исключительной ситуации, когда проверка статуса возврата хоть чем-то лучше Let It Crash. Пока Вы приводите примеры штатного поведения… Применять к ним термины "ошибка" и "исключение", на мой взгляд, в принципе некорректно. В случае же исключительной ситуации единственное возможное поведение — это как можно быстрее привести в систему в рабочее состояние после сбоя.
Я знаю, просто divan0 смешивает запасной вариант и исключение на уровне понимания.
В зависимости от контекста одна и та же вызов функции может приводить и к тому и к другому, но концептуально это разные вещи. Поэтому во многих языках библиотечные функции имеют 2 формы: одна возвращает статус и результат, а вторая — только результат, а в случае ошибки кидает исключение.
В Go для второго случая придётся в куче мест писать
if err != nil { panic(err) }Вы понимаете, что такое исключительная ситуация?
То это не исключение, т.к. у нас есть другой файл.
То это не исключение, т.к. у нас есть дефолтное значение.
То это прописанная бизнес-логика.
Всё, что Вы приводите в качестве примера, строго говоря, не имеет отношения к исключительным ситуациям. Т.к. исключение — это неожидаемое поведение, обработка которого не описана в требованиях.
См. мой комментарий чуть выше. Разработчики отказоустойчивых систем с Вами категорически не согласны :-)
Самое интересное, что для большинства практических применений лучшая обработка ошибок будет в подходе "Let It Crash". Проверять коды возврата, ловить исключения, — это всё равно путь в никуда при обработке настоящих исключительных ситуаций (таких, которые не должны произойти), в нетривиальной программе вы по-любому пропустите обработку какого-нибудь кода возврата, при этом мало того, что код будет захламлён, так ещё и варианта восстановления именно на этот случай не будет.
Разбаловала Вас дешёвая ОЗУ, я помню лет 8 назад сталкивался по работе с аналогичной задачей… И свелась она к тому, чтобы построчно читать XML и как только нужный узел был прочитан, он сразу отправлялся в обработку, освобождая память для следующего. В итоге программа не выходила за 100 Mb по памяти (точное значение уже не помню), несмотря на 2 Gb обрабатываемого XML.
Всё очень относительно… сейчас плата, умещающаяся на ладони, может быть в разы мощнее персонального компьютера 10-летней давности.
Тут скорее надо определиться с восприятием, что такое эмбед? Это ограниченные ресурсы или ограниченный размер… Если второе, то ресурсов хватит практически на любой современный ЯП.
Вот интересно, кстати, почему? Очевидно, что сейчас в мире уже гораздо больше embedded систем, чем программистов, способных написать для них безопасный код на C.
По факту хвостовая рекурсия раскрывается в тот же ассемблерный код, что и обычный цикл.
А в комментарии, видимо, имеется в виду проблема, когда стек заканчивается. Рекурсия, использующая стек, обычно применяется в императивных языках, т.к. там может не быть tail-call optimization. Ну либо в крайне редком подмножестве алгоритмов, где без древовидной рекурсии вообще никак не обойтись.
В этом плане да, согласен.
Спасибо, кэп.
В комментарии, на который Вы в начале ветки ответили, обратного и не утверждалось )))
Индекс TIOBE строится на основе количества запросов к поисковикам. Т.е., строго говоря, он не имеет корреляции с распространённостью языка или с количеством кода на нём написанном.
Для научных расчётов я бы ещё обратил внимание на Julia, IDE для неё тоже есть весьма приличная.