Здравствуйте, Terix, Вы писали:
CD>>Поздравляю! Вы изобрели монаду! CD>>Типа Error Monad.
CD>>Языки в которых такое есть: CD>> CD>> Haskell CD>> F# CD>> OCaml CD>> Idris CD>>
CD>>Насчёт первых двух уверен на 100%, насчёт остальных — поменьше, поскольку не работаю с ними. CD>>Наверняка есть и другие языки с поддержкой монад вообще и монады ошибок в частности.
T>А в этих самых монадах есть неявное преобразование к инту, как в задумке в первом посте?
Господи Боже, ну конечно же нет! Вы, должно быть, сбрендили — неявные преобразования! Скажете тоже...
Здравствуйте, MTD, Вы писали:
MTD>За время моей трудовой деятельности я участвовал как в проектах использующих исключения, так и в проектах использующих коды ошибок. Сейчас я больше склоняюсь ко второму подходу, так как исключения действительно часто становятся этаким неявным goto, кроме того использование их в многопоточном и асинхронном коде становится болью. С другой стороны, отказ от проверок кодов возврата тоже плохо. Давайте пофантазируем как можно было бы организовать идеальную обработку ошибок. Я пока придумал объеденить оба подхода через дополнительную сущность — ну пусть будет поток выполнения (ExecutionFlow), экземпляр которого имеет каждый поток. Данная сущность неявно присутствует в вызове каждой функции, хранит стек вызовов и флажок ошибки. Проще проиллюстрировать псевдокодом:
MTD>
MTD>int plus2([неявно ExecutionFlow executionFlow], int x)
MTD>{
MTD> if (x < 10)
MTD> return RangeError("bla-bla"); // Выход из функции и установка ошибки executionFlow.setError(RangeError("bla-bla"))
MTD> return x + 2; // Просто выход и возврат значения, неявно оборачивается в ReturnValue<int>
MTD>}
MTD>void foo() { ... }
MTD>
MTD>Вызывающий код:
MTD>
MTD>var y = foo(5);
MTD>if (y.success) // проверка, также устанавливает в объекте ReturnValue<int> и ExecutionFlow флажок checked = true
MTD> int z = bar(y); // неявно приводится к int, если перед приведением не проверить success, то программа пишет стек и завершается
MTD>if (!foo().success)
MTD> print(executionFlow.stackTrace());
MTD> var error = executionFlow.error();
MTD> print(error.message())
MTD> return error
MTD>foo(); // не проверили success, флажок не установился, значит при вызове следующей функции (любой) будет стек и аварийное завершение
MTD>
MTD>Как считаете имеет право на жизнь? Есть ли языки с таким подходом к обработке ошибок?
Имеет, конечно, но проблемно из-за того, что заставляет программиста проверять ошибки в рантайме, а надо бы свалить это на компилятор.
Either<Error, Int> plus2([неявно ExecutionFlow executionFlow], int x)
{
if (x < 10)
return Left(RangeError("bla-bla"));
return Right(x + 2);
}
int z = plus2(4).getOrElse { throw WtfException } //если произошла любая ошибка, бросаем исключение. Фигурные скобки - это лямбда.
int z = plus2(4) match { // если RangeError, бросаем исключение, если другая ошибка - возвращаем -1
case Right(result) => result
case Left(RangeError(msg)) => throw WtfException("Error: " + msg)
case Left(otherError) => -1
}
Either<Error, Int> result = plus2(4)
if (result.isLeft) {
// ошибка
}
return result.get() // бросит исключение, если ошибка
Здравствуйте, Terix, Вы писали:
T>Ну, значит, монады не переизобретены, а улучшены
Не улучшены, а ухудшены (протечка абстракции).
Фокус монады состоит в том, что вся арифметика выполняется внутри монады, — там просто не нужны преобразования.
Здравствуйте, night beast, Вы писали:
NB>да, но с исключениями это приходится писать явно. NB>или Optional это просто дополнение к исключениям? тогда нормально. NB>почему-то решил что как альтернатива предлагается.
Если использовать Option как монаду, то — либо bar(string) просто не получит управление, либо bar(Option<string>) получит завёрнутые данные с кодом возврата, потому что оно знает, что с этим делать.
Обрати внимание на сигнатуры.
Можно сказать, что это и есть "писать явно".
Здравствуйте, Qbit86, Вы писали:
Q>Механизм исключений в языке при этом в каком-то виде может быть (раскрутка стека), просто они неперехватываемые (panic).
Можно примеры языков, где панику невозможно перехватить?
Здравствуйте, DarkEld3r, Вы писали:
Q>>Механизм исключений в языке при этом в каком-то виде может быть (раскрутка стека), просто они неперехватываемые (panic).
DE>Можно примеры языков, где панику невозможно перехватить?
Ну и чтобы два раза не вставать: в Rust так же есть возможность перебросить пойманную панику и проверить тип. То есть, возможности более-менее соответствуют исключениям, разве что пользоваться этим менее удобно, ну и "не принято".
It is not recommended to use this function for a general try/catch mechanism. The `Result` type is more appropriate to use for functions that can fail on a regular basis. Additionally, this function is not guaranteed to catch all panics, see the "Notes" section below.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, DarkEld3r, Вы писали:
DE>>есть std::panic::catch_unwind.
Q>
It is not recommended to use this function for a general try/catch mechanism. The `Result` type is more appropriate to use for functions that can fail on a regular basis. Additionally, this function is not guaranteed to catch all panics, see the "Notes" section below.
Ну а я что написал выше? Не рекомендуется, да. Но использовать-то можно.
Если смущает примечание, то оно вообще о другом. В плюсах такое, кстати, тоже есть, только не в стандарте, но популярные компиляторы реализуют что-то типа -fno-exceptions. И если мы пишем приложение, а не библиотеку, то стратегию обработки паники можем контролировать.
В любом случае, Rust не входит в список языков, где невозможно перехватить панику. И мне всё ещё интересно есть ли такие языки.
Здравствуйте, vsb, Вы писали:
MTD>>Вот это и есть синхронный код, да он притворяется асинхронным, но сущность его именно такова. vsb>Всё наоборот. Это асинхронный код, который притворяется синхронным.
И опять неверно. ))
Это кооперативная многозадачность вместо вытесняющей.
Приведенный код синхронный, разумеется, ничего асинхронного в нём нет.
>> В настоящем асинхронном коде таких явных точек синхронизации нет
Вот еще. А если я использую библиотеку кооперативной многозадачности? Например, как в Windows 3.x при вызове любой операции ввода-вывода? Там абсолютно то же самое происходило, только не надо было явно указывать await. Т.е. весь ввод-вывод был синхронный для логического потока исполнения, но асинхронный в "масштабе" компьютера.
vsb>после выполнения операции вызывается обработчик — это позволяет лучше нагрузить процессор не тратя время на ожидание.
Процессор и не тратит время на ожидание.
Вся дикость вот этого сниппета const x = await something() имеет корнями высокую цену ядерных переключений потоков в СОВРЕМЕННЫХ процах с защищённой памятью и ничего более. Если бы ядро ОС переключало потоки с достаточной эффективностью, то можно было бы ожидать выполнение операции something ср-вами самой ОС (через хендл асинхронной операции). Но такой код ведь зовут синхронным, верно?
vsb>Это не точка синхронизации. Это указание кода, который будет выполнен при получении результата.
Это и есть точка синхронизации задач прямо по-определению.
Дотнетный Task — это всего-навсего юзер-спейсный аналог процесса (потока) и ничего более.
vsb>Собственно поинт в том, что если в языке есть удобные средства для работы с асинхронным кодом
Нету. Приведенный код — не асинхронный.
Прибит гвоздями к объекту Task, который сам по себе достаточно тяжеловесен.
А необходимость объявлять окружающий этот сниппет метод как async — так вообще дичь и ха-ха 3 раза. ))
Для сравнения, ни одна нейтивная библиотека кооперативной многозадачности этого не требует.
Кароч, это всё нубство на ровном месте. Костыль на безрыбье, чтобы раком не встать.
vsb>(в JavaScript, Kotlin, вроде в C# тоже такое есть), то исключения прекрасно подходят для работы с ошибками. Если таких средств нет, с асинхронным кодом работать очень неудобно в любом случае.
В дотнете и так неудобно с асинхронным кодом работать. Например, ты не можешь написать свой независимый механизм кооперативной диспетчеризации и использовать его аналогично ср-вами языка. Ну вот не нравятся мне тяжеловесные и тупые Task, у меня есть своя легковесная lock-free реализация Promise/Future, которая на порядки удобней (потому что сам этот подход хорошо проработан во многих языках). Вот как мне использовать продолжения от моих Future в аналогичном синтаксисе await, ы? )) Аж никак. Только явно описывать продолжения на анонимных ф-иях, что есть дичь. Но зато это будет тот самый настоящий асинхронный код, со всеми его "плюшками" (в плохом смысле этого слова).
Здравствуйте, Terix, Вы писали:
T>В предыдущем посте написано, что поведение программы не будет меняться в зависимости от того, какой код сгенерировал компилятор — синхронный, или асинхронный. А оно будет меняться
Не будет. Если на каждый дотнетный Task создавать по системному потоку и в месте указания оператора await ожидать синхронно завершения операции, то поведение будет полностью идентичным. Разница будет лишь в эффективности.
T>потому что в случае асинхронного кода, что-то может вклиниться между something и следующей строкой, а в случае синхронного кода — не может.
В случае синхронного "вклиниться" может другой поток, исполняющий другой Task. Как и сейчас, собсно.
отдельная операция
для списка файлов
отрываем файл
пишем данные
закрываем
1. ошибка: не удаётся записать файл (нет прав, файл уже есть, временно нет связи, кончилась квота, ...)
1.1. остановить обработку списка и кинуть исключение
1.2. продолжить обработку списка, отложив эту ошибку в список проблем.
1.3. удалить то что уже сделано и вернуть ошибку операция не выполнена
1.4. другой вариант
2. ошибка: Операция для отдельного файла затянулась, (вместо секунд для таких же файлов, на часы)
2.1. не обращать внимания на зависания.
2.2. контролировать время обработки списка и диагностировать отклонения скорости.
2.3. добавить timeout. в случае превышение отложить в конец списка.
2.4. другое
3. отложенная ошибка: файл успешно записан в кэш системы, но не на диск. а вот при записи на диск записало с ошибками или не записало вообще.
3.1. забить
3.2. мониторить системные события
3.3. добавить процедуру проверки и дообработки списка
3.4. другое
4. событие: внешние прерывание (отмена операции)
4.1. прервать по завершению обработки последнего файла
4.2. прервать немедленно и удалить последний файл
4.3. прервать немедленно и удалить то что уже обработали до этого
4.4. не обращать внимания
4.5. другое
5. событие: исключение в коде (что пошло не так)
5.1. всё прервать и выкинуть дальше
5.2. добавить в список проблем и попробовать остальные файлы в списке
5.3. попробовать заново с последнего файла
5.4. другое
Какой из современных языков позволяет обрабатывать подобные ситуации не усложняя исходный код лишними языковыми конструкциями.
В идеале как в CSS указываем слекторами где и правилами меняем поведение по умолчанию на нужное.
Здравствуйте, Jack128, Вы писали:
V>>Вот как мне использовать продолжения от моих Future в аналогичном синтаксисе await, ы? )) Аж никак. J>Вообще то можно. Ключевые слова: AsyncMethodBuilder, Awaiter pattern
Здравствуйте, DarkEld3r, Вы писали:
DE>В любом случае, Rust не входит в список языков, где невозможно перехватить панику. И мне всё ещё интересно есть ли такие языки.
Тот на котором мидори написана. http://joeduffyblog.com/2016/02/07/the-error-model/
Our overall solution was to offer a two-pronged error model. On one hand, you had fail-fast – we called it abandonment – for programming bugs. And on the other hand, you had statically checked exceptions for recoverable errors. The two were very different, both in programming model and the mechanics behind them. Abandonment unapologetically tore down the entire process in an instant, refusing to run any user code while doing so. (Remember, a typical Midori program had many small, lightweight processes.) Exceptions, of course, facilitated recovery, but had deep type system support to aid checking and verification.
... << RSDN@Home 1.0.0 alpha 5 rev. 0>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн