Здравствуйте, MozgC, Вы писали:
MC>Может не надо ничего фантазировать, а надо просто научиться пользоваться исключениями и многопоточностью?
Исключения — плохая вещь, безотносительно того, умеешь ей пользоваться или нет.
Ну то есть как плохая, просто отличить плохой код с исключениями от хорошего кода с исключениями — сложно. Рассуждать о программах с исключениями — сложно.
Здравствуйте, vsb, Вы писали:
vsb>любой цикл или условный оператор это "неявный goto".
Цикл и иф имеют точное место продолжения выполнения. Исключения и гото — мы не знаем, куда летим. Поэтому циклы и ифы — структурные операторы, а исключения и гото — нет.
Здравствуйте, MTD, Вы писали:
MTD>Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок. Я пока придумал объеденить оба подхода через дополнительную сущность — ну пусть будет поток выполнения (ExecutionFlow), экземпляр которого имеет каждый поток. Данная сущность неявно присутствует в вызове каждой функции, хранит стек вызовов и флажок ошибки.
Обработке ошибок присуща природная сложность и не очень понятно, как можно было бы серьезно упростить жизнь программиста. Сейчас лично я думаю так. Часть ошибок, которые сейчас являются рантайм-ошибками, должна перейти на стадию компиляции. Пример — NRE. Часть ошибок должна стать фатальными и не обрабатываться вообще (stack overflow, недостаток памяти). Часть ошибок можно отсечь частичными функциями — не исключение при вызове "log(-1)", а ошибка компиляции. Оставшиеся обрабатывать а-ля Rust — возвращать Result<Type> вместо Type.
MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?
Как справедливо заметили выше, это похоже на error monad. Так что да, где-то используется.
Здравствуйте, AlexRK, Вы писали:
ARK>Здравствуйте, vsb, Вы писали:
GZ>>>>Остаток процедуры будет запущен после выполнения асинхронно функции MTD>>>Вот это и есть синхронный код, да он притворяется асинхронным, но сущность его именно такова. vsb>>Всё наоборот. Это асинхронный код, который притворяется синхронным.
ARK>Семантически этот код — именно синхронный. То, что компилятор где-то внутри генерит — это просто деталь реализации и не более. Если я возьму другой компилятор, который будет создавать синхронный код в этом месте, то не изменится НИЧЕГО — ни код, ни поведение программы.
Нет, это не так. Синхронный код заблочит поток и будет ничего не делать и ждать, ждать, ждать и так пока не закончится вызов something(), после чего перейдёт к следующей строке. Между something() и следующей строкой не вклинится другой код. А асинхронный код вернёт управление сразу, сразу начнёт выполняться какой-то там другой код, а то, что после вызова something(), выполнится отдельно потом.
CD>Поздравляю! Вы изобрели монаду! CD>Типа Error Monad.
CD>Языки в которых такое есть: CD> CD> Haskell CD> F# CD> OCaml CD> Idris CD>
CD>Насчёт первых двух уверен на 100%, насчёт остальных — поменьше, поскольку не работаю с ними. CD>Наверняка есть и другие языки с поддержкой монад вообще и монады ошибок в частности.
А в этих самых монадах есть неявное преобразование к инту, как в задумке в первом посте?
Здравствуйте, Terix, Вы писали:
ARK>>Семантически этот код — именно синхронный. То, что компилятор где-то внутри генерит — это просто деталь реализации и не более. Если я возьму другой компилятор, который будет создавать синхронный код в этом месте, то не изменится НИЧЕГО — ни код, ни поведение программы.
T>Нет, это не так. Синхронный код заблочит поток и будет ничего не делать и ждать, ждать, ждать и так пока не закончится вызов something(), после чего перейдёт к следующей строке. Между something() и следующей строкой не вклинится другой код. А асинхронный код вернёт управление сразу, сразу начнёт выполняться какой-то там другой код, а то, что после вызова something(), выполнится отдельно потом.
Это все я знаю. Я говорю про семантику кода, а не про детали конкретной реализации. Блин, вроде всё в предвдущем посте расписал — всё равно люди не понимают.
Вот этот код — "a = f() + 3;" — синхронный или асинхронный? (Важное уточнение для прибитых к контексту людей: это не C#.)
Семантически — синхронный, то есть выглядит и взаимодействует с другим кодом как синхронный.
Реализован как синхронный или асинхронный? Может быть и так, и так. На текущем уровне абстракции нам это пофигу.
ARK>Это все я знаю. Я говорю про семантику кода, а не про детали конкретной реализации. Блин, вроде всё в предвдущем посте расписал — всё равно люди не понимают.
В предыдущем посте написано, что поведение программы не будет меняться в зависимости от того, какой код сгенерировал компилятор — синхронный, или асинхронный. А оно будет меняться, потому что в случае асинхронного кода, что-то может вклиниться между something и следующей строкой, а в случае синхронного кода — не может.
Здравствуйте, Terix, Вы писали:
ARK>>Это все я знаю. Я говорю про семантику кода, а не про детали конкретной реализации. Блин, вроде всё в предвдущем посте расписал — всё равно люди не понимают.
T>В предыдущем посте написано, что поведение программы не будет меняться в зависимости от того, какой код сгенерировал компилятор — синхронный, или асинхронный. А оно будет меняться, потому что в случае асинхронного кода, что-то может вклиниться между something и следующей строкой, а в случае синхронного кода — не может.
Да, согласен, упустил этот момент. Если говорить только о C#.
MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?
Обязательные проверки с одной стороны — добро, по очевидным причинам. С другой стороны нередко они просто мешают и захламляют код и тратят время и нервы.
Тут ниже уже отписались по этому поводу, но те не менее есть такой вариант.
Соответственно функция принимает один Option и возвращает другой. Таких операций можно сделать много, но в результате у нас будет какой-то Option.
У Option есть метод get, который кинет эксепшн, если какая-то из операций прошла плохо.
Если мы пробуем провести операцию на уже побитом Option, она проводится не будет, а нам молча вернут ещё один дефектный Option.
Преимуществом тут является, что не нужно мучаться с эксепшнами и то, что не надо проверять ошибки на каждый чих, а проверить только там, где нужно.
Недостатком то, что примитивный тип приходится оборачивать и код получается непривычным
Здравствуйте, Terix, Вы писали:
T>Если мы пробуем провести операцию на уже побитом Option, она проводится не будет, а нам молча вернут ещё один дефектный Option. T>Преимуществом тут является, что не нужно мучаться с эксепшнами и то, что не надо проверять ошибки на каждый чих, а проверить только там, где нужно.
теряется информация об ошибке и месте/условиях ее появления.
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, Terix, Вы писали:
T>>Если мы пробуем провести операцию на уже побитом Option, она проводится не будет, а нам молча вернут ещё один дефектный Option. T>>Преимуществом тут является, что не нужно мучаться с эксепшнами и то, что не надо проверять ошибки на каждый чих, а проверить только там, где нужно.
NB>теряется информация об ошибке и месте/условиях ее появления.
Сейчас в мейнстримных языках, в том месте, где произойдёт ошибка, будет сгенерирован эксепшн. Метод map его поймает и сохранит для потомков. В эксепшне будет написано где, что и как. Эксепшн BadOptionException обернёт этот эксепшн и подробности можно будет получить.
Здравствуйте, Terix, Вы писали:
T>>>Если мы пробуем провести операцию на уже побитом Option, она проводится не будет, а нам молча вернут ещё один дефектный Option. T>>>Преимуществом тут является, что не нужно мучаться с эксепшнами и то, что не надо проверять ошибки на каждый чих, а проверить только там, где нужно.
NB>>теряется информация об ошибке и месте/условиях ее появления.
T>Сейчас в мейнстримных языках, в том месте, где произойдёт ошибка, будет сгенерирован эксепшн. Метод map его поймает и сохранит для потомков. В эксепшне будет написано где, что и как. Эксепшн BadOptionException обернёт этот эксепшн и подробности можно будет получить.
допустим.
как решаются проблемы с нарушением инвариантов объекта при исключении?
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, Terix, Вы писали:
T>>>>Если мы пробуем провести операцию на уже побитом Option, она проводится не будет, а нам молча вернут ещё один дефектный Option. T>>>>Преимуществом тут является, что не нужно мучаться с эксепшнами и то, что не надо проверять ошибки на каждый чих, а проверить только там, где нужно.
NB>>>теряется информация об ошибке и месте/условиях ее появления.
T>>Сейчас в мейнстримных языках, в том месте, где произойдёт ошибка, будет сгенерирован эксепшн. Метод map его поймает и сохранит для потомков. В эксепшне будет написано где, что и как. Эксепшн BadOptionException обернёт этот эксепшн и подробности можно будет получить.
NB>допустим. NB>как решаются проблемы с нарушением инвариантов объекта при исключении?
Да, пожалуй, что никак. Надо руками, как и в случаях с исключениями.
Здравствуйте, Terix, Вы писали:
NB>>допустим. NB>>как решаются проблемы с нарушением инвариантов объекта при исключении?
T>Да, пожалуй, что никак. Надо руками, как и в случаях с исключениями.
ну с исключениями хотя бы понятно где бахнуло, а при Optional нарваться на последствия можно в совершенно не связанном с этим Optional месте.
Здравствуйте, Terix, Вы писали:
NB>>ну с исключениями хотя бы понятно где бахнуло, а при Optional нарваться на последствия можно в совершенно не связанном с этим Optional месте.
Ты имеешь в виду такой случай? T>
T>Option<int> a = plus(a); //тут ошибка
T>Option<string> b = getWeirdString(a); //Внутри getWeirdString мы делаем get у Option и ловим эксепшн
нет. что-то вроде такого:
Option<int> a = x.foo(); //тут ошибка
...
x.bar(); //пытаемся работать с кривым x
Здравствуйте, night beast, Вы писали:
NB>Здравствуйте, Terix, Вы писали:
NB>>>ну с исключениями хотя бы понятно где бахнуло, а при Optional нарваться на последствия можно в совершенно не связанном с этим Optional месте.
NB>Ты имеешь в виду такой случай? T>>
T>>Option<int> a = plus(a); //тут ошибка
T>>Option<string> b = getWeirdString(a); //Внутри getWeirdString мы делаем get у Option и ловим эксепшн
NB>
NB>нет. что-то вроде такого: NB>
NB>Option<int> a = x.foo(); //тут ошибка
NB>...
NB>x.bar(); //пытаемся работать с кривым x
NB>
Это ведь то же самое, что
int a = -1;
try {
a = x.foo();
} catch(Exception e) {
Log.Info("нехорошо вышло, да"); //хрень какая-то с a вышла, но это нас не очень волнует
}
x.bar(); //а обработать проблему с кривым иксом в блоке catch мы забыли
Здравствуйте, Terix, Вы писали:
T>Это ведь то же самое, что T>
T>int a = -1;
T>try {
T> a = x.foo();
T>} catch(Exception e) {
T> Log.Info("нехорошо вышло, да"); //хрень какая-то с a вышла, но это нас не очень волнует
T>}
T>x.bar(); //а обработать проблему с кривым иксом в блоке catch мы забыли
T>
да, но с исключениями это приходится писать явно.
или Optional это просто дополнение к исключениям? тогда нормально.
почему-то решил что как альтернатива предлагается.
Здравствуйте, MTD, Вы писали:
MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок.
Тут всё разобрано по косточкам.
Много букв, но ничего лучше про обработку ошибок я не видел. http://joeduffyblog.com/2016/02/07/the-error-model/
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Здравствуйте, night beast, Вы писали:
NB>теряется информация об ошибке и месте/условиях ее появления.
`Option<T>` (сумма `Some<T>` и `None`) просто «частный случай» типа `Either<L, R>` или `Result<T, E>` (сумма `Some<T>` и `Error<E>`). `Option<T>` используется, когда причина ошибки неважна или единственна (например, ключ в словаре не найден). Если вариантов ошибки может быть больше, то можно возвращать `Result<T, E>`, и класть туда `Error<E>`, где `E` — информация об ошибках, туда можно сохранить контекст возникновения (фаза луны, состояние программы, etc.).
Обычно это удобно в языках с паттерн-матчингом или монадами в стандартной библиотеке. Если адаптировать этот подход на какой-нибудь ООП-язык типа C#, то в `Result<T, E>` ты просто кладёшь наследника от `Exception`, например, который замыкает в себе информацию об условиях появления ошибки.