Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 11:45
Оценка: -1
За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto, кроме того использование их в многопоточном и асинхронном коде становится болью. С другой стороны, отказ от проверок кодов возврата тоже плохо. Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок. Я пока придумал объеденить оба подхода через дополнительную сущность — ну пусть будет поток выполнения (ExecutionFlow), экземпляр которого имеет каждый поток. Данная сущность неявно присутствует в вызове каждой функции, хранит стек вызовов и флажок ошибки. Проще проиллюстрировать псевдокодом:

int plus2([неявно ExecutionFlow executionFlow], int x)
{
    if (x < 10)
        return RangeError("bla-bla"); // Выход из функции и установка ошибки executionFlow.setError(RangeError("bla-bla"))

    return x + 2; // Просто выход и возврат значения, неявно оборачивается в ReturnValue<int>
}

void foo() { ... }


Вызывающий код:

var y = foo(5);
if (y.success) // проверка, также устанавливает в объекте ReturnValue<int> и ExecutionFlow флажок checked = true
   int z = bar(y); // неявно приводится к int, если перед приведением не проверить success, то программа пишет стек и завершается

if (!foo().success)
   print(executionFlow.stackTrace());
   var error = executionFlow.error();
   print(error.message())
   return error

foo(); // не проверили success, флажок не установился, значит при вызове следующей функции (любой) будет стек и аварийное завершение


Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?
Re: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 11:53
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>
MTD>if (!foo().success)
MTD>


Еще дополнение — нельзя просто написать foo.success и забить, надо обязательно проверить if, за этим следит компилятор и чтобы жизнь медом не казалось if не должен быть пустым. Чтобы просто проигнорировать проверку надо вместо success вызвать что-то вроде foo().IWantToSkipThisCheck
Re: Обработка ошибок
От: vsb Казахстан  
Дата: 26.09.17 12:02
Оценка: +2 -2
Пока что не встречал ситуаций, когда мне не хотелось бы использовать исключения. Поэтому считаю коды возврата отвратительной идеей. Про асинхронный код:

try {
  const x = await something();
} catch (e) {
  console.warn(e);
}


Какие проблемы с многопоточным кодом или с goto я тоже не знаю, любой цикл или условный оператор это "неявный goto".
Отредактировано 26.09.2017 12:09 vsb . Предыдущая версия .
Re: Обработка ошибок
От: Sinix  
Дата: 26.09.17 12:07
Оценка:
Здравствуйте, MTD, Вы писали:

MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?

Зависит от окружения. Для шарпа, скажем, почти всегда сработает TryXxx + парный метод с исключениями. Все остальные способы требуют гораздо больше телодвижений и ведут к адскому лапшекоду — приходится или городить обёртки для чужого api, или поддерживать несколько стилей обработки ошибок.

Я очень сомневаюсь, что получится совместить явную проверку результатов выполнения и компактность кода. Т.е. или ищем язык с автоматом проверяемыми предусловиями, или велкам в копипасту if(error) по всему коду.
Отредактировано 26.09.2017 12:09 Sinix . Предыдущая версия .
Re: Обработка ошибок
От: GlebZ Россия  
Дата: 26.09.17 12:09
Оценка: 1 (1) +1
Здравствуйте, MTD, Вы писали:

MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto, кроме того использование их в многопоточном и асинхронном коде становится болью.

Это два разных подхода с совершенно различными целями и средствами. В случае кодов — это обычно ветвление бизнес-логики, в результате которого нужно продолжить сценарий по какой-то отдельной ветке. В случае реальной ошибке — это обработка через исключения, в котором необходимо корректно убить само приложение которое в неизвестно каком состоянии (с записью ошибки в лог), либо максимальная очистка контекста вызова в случае серверного приложения (с записью ошибки в лог).
Re[2]: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 12:24
Оценка: -1
Здравствуйте, vsb, Вы писали:

vsb>Про асинхронный код:


vsb>
vsb>try {
vsb>  const x = await something();
vsb>} catch (e) {
vsb>  console.warn(e);
vsb>}
vsb>


Это не асинхронный код
Re: Обработка ошибок
От: kov_serg Россия  
Дата: 26.09.17 12:29
Оценка:
Здравствуйте, MTD, Вы писали:

Что исключения что коды ошибок не решают проблему, а порождают новые.
Если код описывает последовательность действий, при условии что всё гладко то код простой.
Если что-то пошло не так, то надо выполнять действия которые обычно вне компетенции данного кода и вне знаний вызывающего.
Поэтому любая попытка обрабатывать коды внутри приводит к увеличению сложности, а снаружи к увеличению кол-ва абстракций.
А еще код наровит выделять разные ресурсы, которые кто-то должен освобождать и останавливать (если дочерние потоки или всякие подписки).

Соответственно языку следовало бы иметь неявно ExecutionFlow, который определял бы определял политику реакции на не штатные ситуации. И через него же выделять ресурсы, что память что дочерние потоки исполнения. И операторы которые явно задают как реагировать на нештатное поведение. И политику и мониторинг выделения ресурсов туда же засунуть, например выполнять не более 100мс (или даже в тактах) и памяти не более 10Мб и не более 4 потоков.
  value=method1(args) or method2(args);
  action1(); // если код возврата никем не обрабатывается то компилятор автоматически делает примерно такое
  // { var res=action1(); if (res && executionFlow.NoErrorAllowed && !executionFlow.DisableThisError(here)) return executionFlow.ReturnError(here,res,"action1" ... ); }
  action2() or action2b();
  action3() or ignore;
  action4()


MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto, кроме того использование их в многопоточном и асинхронном коде становится болью. С другой стороны, отказ от проверок кодов возврата тоже плохо. Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок. Я пока придумал объединить оба подхода через дополнительную сущность — ну пусть будет поток выполнения (ExecutionFlow), экземпляр которого имеет каждый поток. Данная сущность неявно присутствует в вызове каждой функции, хранит стек вызовов и флажок ошибки. Проще проиллюстрировать псевдокодом:


MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?

Право на жизнь имеет любой подход. Erlang знатно обрабатывает ошибки.
Re: Bind
От: Qbit86 Кипр
Дата: 26.09.17 12:31
Оценка: +1
Здравствуйте, MTD, Вы писали:

MTD>Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок.


Всё давно придумано. В Rust, насколько мне известно, нет исключений. В языках с поддержкой алгебраических типов данных это выражается типами-суммами Option<T> (aka Maybe) и Result<T, E>. В обычных языках это можно эмулировать а-ля «коды ошибок на стероидах». Возвращаемые данные нужно явно unwrap'ить (ну или лучше использовать монадический bind) — в любом случае нельзя «пропустить» проверку. В случае ошибки её контекст тоже сохраняется, то есть это не просто целочисленный код. Механизм исключений в языке при этом в каком-то виде может быть (раскрутка стека), просто они неперехватываемые (panic).
Глаза у меня добрые, но рубашка — смирительная!
Re[3]: Обработка ошибок
От: vsb Казахстан  
Дата: 26.09.17 12:44
Оценка:
Здравствуйте, MTD, Вы писали:

vsb>>Про асинхронный код:


vsb>>
vsb>>try {
vsb>>  const x = await something();
vsb>>} catch (e) {
vsb>>  console.warn(e);
vsb>>}
vsb>>


MTD>Это не асинхронный код


Почему это не асинхронный? something() возвращает асинхронный результат.
Re[4]: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 12:48
Оценка:
Здравствуйте, vsb, Вы писали:

vsb>Почему это не асинхронный? something() возвращает асинхронный результат.


Потому что ты синхронно дожидаешься результата. Если бы ты продолжил выполнение другого кода с неблокирующимися вызовами, то да код стал бы асинхронным и внезапно стало бы непонятно, как работать с исключениями.
Re[5]: Обработка ошибок
От: vsb Казахстан  
Дата: 26.09.17 13:05
Оценка: +1
Здравствуйте, MTD, Вы писали:

vsb>>Почему это не асинхронный? something() возвращает асинхронный результат.


MTD>Потому что ты синхронно дожидаешься результата.


Ничего я не дожидаюсь.

> Если бы ты продолжил выполнение другого кода с неблокирующимися вызовами, то да код стал бы асинхронным и внезапно стало бы непонятно, как работать с исключениями.


Это асинхронный код с неблокирующимся вызовом. Он отдаёт управление сразу по достижении слова await.
Re[6]: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 13:26
Оценка: :)
Здравствуйте, vsb, Вы писали:

vsb>Ничего я не дожидаюсь.


Ты что-то путаешь — await означает ждать и так он себя в известных мне языках и ведет, кстати, сейчас мы про какой язык говорим?
Re[7]: Обработка ошибок
От: GlebZ Россия  
Дата: 26.09.17 13:32
Оценка: +1
Здравствуйте, MTD, Вы писали:

MTD>Ты что-то путаешь — await означает ждать и так он себя в известных мне языках и ведет, кстати, сейчас мы про какой язык говорим?

await — это генерация компилятором конечного автомата с выходом из метода и освобождения потока и входом при окончании исполнения асинхронного метода.
Re[8]: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 13:46
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>await — это генерация компилятором конечного автомата с выходом из метода и освобождения потока и входом при окончании исполнения асинхронного метода.


Да понятно, в приведенном коде точка синхронизации — получение х, пока его не получим на следующую строчку не попадем. Я не прав?
Re[9]: Обработка ошибок
От: GlebZ Россия  
Дата: 26.09.17 14:14
Оценка: +1
Здравствуйте, MTD, Вы писали:


GZ>>await — это генерация компилятором конечного автомата с выходом из метода и освобождения потока и входом при окончании исполнения асинхронного метода.


MTD>Да понятно, в приведенном коде точка синхронизации — получение х, пока его не получим на следующую строчку не попадем. Я не прав?

Безусловно. Остаток процедуры будет запущен после выполнения асинхронно функции и при наличии кванта времени в диспетчере. Поэтому это действительно асинхронное неблокирующеся исполнение.
Re[10]: Обработка ошибок
От: MTD https://github.com/mtrempoltsev
Дата: 26.09.17 14:23
Оценка: +2
Здравствуйте, GlebZ, Вы писали:

GZ>Остаток процедуры будет запущен после выполнения асинхронно функции


Вот это и есть синхронный код, да он притворяется асинхронным, но сущность его именно такова. В настоящем асинхронном коде таких явных точек синхронизации нет, после выполнения операции вызывается обработчик — это позволяет лучше нагрузить процессор не тратя время на ожидание.
Re[11]: Обработка ошибок
От: vsb Казахстан  
Дата: 26.09.17 14:32
Оценка: +1 -1
Здравствуйте, MTD, Вы писали:

GZ>>Остаток процедуры будет запущен после выполнения асинхронно функции


MTD>Вот это и есть синхронный код, да он притворяется асинхронным, но сущность его именно такова.


Всё наоборот. Это асинхронный код, который притворяется синхронным.

> В настоящем асинхронном коде таких явных точек синхронизации нет, после выполнения операции вызывается обработчик — это позволяет лучше нагрузить процессор не тратя время на ожидание.


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

Собственно поинт в том, что если в языке есть удобные средства для работы с асинхронным кодом (в JavaScript, Kotlin, вроде в C# тоже такое есть), то исключения прекрасно подходят для работы с ошибками. Если таких средств нет, с асинхронным кодом работать очень неудобно в любом случае.
Отредактировано 26.09.2017 14:34 vsb . Предыдущая версия .
Re: Обработка ошибок
От: MozgC США http://nightcoder.livejournal.com
Дата: 26.09.17 14:36
Оценка: 4 (1) +3
Здравствуйте, MTD, Вы писали:

MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto


Вас же, надеюсь, никто заставляет использовать исключения в качестве неявного goto?

MTD>кроме того использование их в многопоточном и асинхронном коде становится болью.


Хз, у нас вроде не становится. В чем боль? Task'и, PLinq, класс Parallel — оборачивают исключение в AggregateException и пробрасывают его в вызывающий код.

Может не надо ничего фантазировать, а надо просто научиться пользоваться исключениями и многопоточностью?

Я по этой теме писал кое что:
http://rsdn.org/forum/dotnet/3606700.1
Автор: MozgC
Дата: 18.11.09

http://rsdn.org/forum/design/4468371.1
Автор: MozgC
Дата: 24.10.11
Re: Обработка ошибок
От: Code Digger Грузия  
Дата: 26.09.17 14:53
Оценка: 10 (1) +1
Здравствуйте, MTD, Вы писали:

MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto, кроме того использование их в многопоточном и асинхронном коде становится болью. С другой стороны, отказ от проверок кодов возврата тоже плохо. Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок. Я пока придумал объеденить оба подхода через дополнительную сущность — ну пусть будет поток выполнения (ExecutionFlow), экземпляр которого имеет каждый поток. Данная сущность неявно присутствует в вызове каждой функции, хранит стек вызовов и флажок ошибки. Проще проиллюстрировать псевдокодом:


MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?


Поздравляю! Вы изобрели монаду!
Типа Error Monad.

Языки в которых такое есть:
  1. Haskell
  2. F#
  3. OCaml
  4. Idris

Насчёт первых двух уверен на 100%, насчёт остальных — поменьше, поскольку не работаю с ними.
Наверняка есть и другие языки с поддержкой монад вообще и монады ошибок в частности.
Re[12]: Обработка ошибок
От: AlexRK  
Дата: 26.09.17 16:55
Оценка: +1 -3 :)
Здравствуйте, vsb, Вы писали:

GZ>>>Остаток процедуры будет запущен после выполнения асинхронно функции

MTD>>Вот это и есть синхронный код, да он притворяется асинхронным, но сущность его именно такова.
vsb>Всё наоборот. Это асинхронный код, который притворяется синхронным.

Семантически этот код — именно синхронный. То, что компилятор где-то внутри генерит — это просто деталь реализации и не более. Если я возьму другой компилятор, который будет создавать синхронный код в этом месте, то не изменится НИЧЕГО — ни код, ни поведение программы.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.