Я так понимаю, если промежуточная операция не удалась, то весь остальной код продолжает работать как ни в чём не бывало, и лишь операции с тем же FacilityID перестают выполняться? Звучит сомнительно
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[15]: Result objects - все-таки победили Exceptions?
·>Я не очень понимаю. Как на ерланге написать код вроде
Язык позволяет и result objects, и exceptions, так что — вот пример.
У здорового Эрланг-программиста код будет выглядеть так:
add(Left, Right) ->
parse(Reft) + parse(Right).
Однако есть довольно заметная секта поклонения тайпчекерам/result objects, у них будет так:
add(Left, Right) ->
case parse(Left) of
{ok, ParsedLeft} ->
case parse(Right) of
{ok, ParsedRight} ->
ParsedLeft + ParsedRight;
{error, Reason} ->
{error, {right, parse_failed, Reason}}
end;
{error, Reason} ->
{error, {left_parse_failed, Reason}}
end.
Я уж и не знаю, как от этого отучать "умудренных опытом программистов на <.вставьте свое.>".
·>Ну вот в случае parse — где им положено?
Там, где эту ошибку можно обработать. Очень часто это на самом высоком уровне, вплоть до "упасть с сообщением об ошибке". Обычно оно так и есть, но в некоторых случаях бывает, например, что можно попробовать другой парсер (в качестве fallback).
·>Про erlang я очень плохо знаю. Поэтому не очень понял суть этого высказывания.
Концепция простая, если разобраться. Представь, что для каждой функции ты можешь задать, сколько раз ее retry'ить, и что делать, если в течение N секунд происходит более M ретраев (классический вариант — ретрайнуть функцию уровнем повыше). Мда, корявое вышло объяснение. Для более осмысленного, лучше прочитать диссер Джо Армстронга, про error handling как раз — https://erlang.org/download/armstrong_thesis_2003.pdf
·>Так сложно вести беседу. Получается: "А вот исключения такие-то", а ты "но вот в криво отформатированном коде всё плохо". Отформатируй код по-другому. Делов-то..
Исключения позволяют писать более простой и понятный код. Как только начинается boilerplate в этих result objects, да умноженное на "копроративное форматирование кода", получается швах.
TB>Я так понимаю, если промежуточная операция не удалась, то весь остальной код продолжает работать как ни в чём не бывало, и лишь операции с тем же FacilityID перестают выполняться? Звучит сомнительно
Почему сомнительно? Зачем проверять что операция не удалась на каждом ее этапе, если в принципе ничего не измениться если я это проверю только тогда, когда мне нужно определиться с ее финальным статусом? В большинстве случаев это вполне допустимо. В данном случае — ну не удалось сохранить файл, покажем юзеру мессагу он ткнет ретрай и сохраним еще раз, или отмену. Что даст осознание факта неудачи прям в тот самый момент? Даст лишь +100500 строк проверки на код ошибки если у нас сериализация здорового объекта. Такая вот крайность. Исключения — это можно сказать противоположная крайность. Истина — она всегда посередине.
Как много веселых ребят, и все делают велосипед...
Re[17]: Result objects - все-таки победили Exceptions?
Здравствуйте, ononim, Вы писали:
O>Почему сомнительно?
Потому что процесс все равно продолжается. С битыми данными, которые мы передаем в функцию не знающую ни про какой FacilityID.
Ну либо весь язык с самого начала дизайнить чтоб он требовал задавать множество FacilityID для каждой операции. Сдается мне что это будет еще и тормознуто — мы проверяем код ошибки не после функции а в начале каждой следующей функции
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[18]: Result objects - все-таки победили Exceptions?
Здравствуйте, SkyDance, Вы писали:
SD>>> {error, {right, parse_failed, Reason}} SD>>> {error, left_parse_failed, Reason} TB>>А почему по-разному написал?
SD>А потому что error-prone, блин.
SD>Исправил, сейчас написано одинаково.
Аааа, я на полном серьезе искал идиоматический подвох, вдруг так и задумано что ошибка парсинга правого и левого операндов это разные ошибки которые по-разному заворачиваются. Строго типизированная корректность же, доведенная до финала
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[17]: Result objects - все-таки победили Exceptions?
Здравствуйте, T4r4sB, Вы писали:
TB>А что такое x, y для начала? Тогда я смогу описать этот код на Расте.
Ровно тот же пример, на который я ссылался в предыдущем посте. TB>В самом простом случае код такой: TB>
Здравствуйте, Sinclair, Вы писали:
S>А без явного указания типа результата никак?
Можно описать тип результата основываясь на типах аргументов.
А сказать "сам выведи по содержимому функции" — нельзя. И это очень хорошо потому что я знаю каково это когда поменял одну строчку в одной функции и из-за этого поползли изменения сигнатуры по всей кодовой базы, и иногда там где вообще не хочется, и не всегда понятно почему.
Один из принципов Раста — весь умный анализ сидит внутри функции. При этом вся инфа для межфункционального анализа зашита в явно прописанной сигнатуре функции.
S> И есть ли возможность вернуть Result<i32, BarError|BazError>?
Можно только если ты сам опишешь нужный таггед юнион и вручную пропишешь мапперы в него. С телефона не напишу
Кстати а A|B|C , (A|B)|C и A|(B|C) это одно и то же или разная фигня?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[19]: Result objects - все-таки победили Exceptions?
Здравствуйте, T4r4sB, Вы писали:
TB>Можно описать тип результата основываясь на типах аргументов. TB>А сказать "сам выведи по содержимому функции" — нельзя. И это очень хорошо потому что я знаю каково это когда поменял одну строчку в одной функции и из-за этого поползли изменения сигнатуры по всей кодовой базы, и иногда там где вообще не хочется, и не всегда понятно почему.
Не вижу проблемы, если указание типов опционально. То есть хочешь — и всё едет. Не хочешь — пишешь явно "здесь должно быть вот так", и дальше этой строчки изменения не поползут.
TB>Можно только если ты сам опишешь нужный таггед юнион и вручную пропишешь мапперы в него. С телефона не напишу
Ну, вот это и мешает писать нормально.
TB>Кстати а A|B|C , (A|B)|C и A|(B|C) это одно и то же или разная фигня?
Одно и то же. И B|(C|A) — тоже.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Result objects - все-таки победили Exceptions?
Здравствуйте, ononim, Вы писали:
S>>Но, как оказалось, народ идеи не понял. Так и вернулись к понятным дебилоидам кодам возврата, т.к. проверяемые исключения осилить не смогли. Так же и в Kotlin их решили не делать. O>Я думаю концепция GetLastError()/errno недооценена O>Но ее надо чутка расширить, параметром facility.И API сделать таким: O>
Я нифига не понял, как это предполагается использоваться.
Что будет, если код раньше поставил ошибку, а код позже решил обнулить ошибку ради проверки результата другого кода?
(см. man strtol, как это выглядит на практике. и он не один такой)
В случае исключений или объектов результатов нет проблемы с перекрытием прошлых ошибок, если нет явного (легче отловить) игнорирования ошибки. А тут как это обеспечивается?
The God is real, unless declared integer.
Re[3]: Result objects - все-таки победили Exceptions?
N>Я нифига не понял, как это предполагается использоваться. N>Что будет, если код раньше поставил ошибку, а код позже решил обнулить ошибку ради проверки результата другого кода?
Видимо разный FalicittID нужен для этого
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[20]: Result objects - все-таки победили Exceptions?
S>Не вижу проблемы, если указание типов опционально. То есть хочешь — и всё едет. Не хочешь — пишешь явно "здесь должно быть вот так", и дальше этой строчки изменения не поползут.
Я разрабатывал язык в котором по умолчанию любая функция шаблонная, которая по контексту определяет какие у неё параметры, сколько их и что она возвращает. Так вот это полный треш когда изменение в одной функции по цепочке приводило к тому, что компилятор что-то своё додумал в другом файле проекта и по итогу в лучшем случае ошибка компиляции, в худшем просто хрень выводится.
А ещё малозаметные проблемы когда компилятор из-за незначительных нюансов для двух наборов параметров навыводил разные перегрузки (ну типа в одном случае он решил передавать копию потому что объект временный, а в другом ссылку), и в бинарнике появляются две почти одинаковые функции. Раздувание бинарника на пустом места.
В языке был механизм явно указывать сигнатуру, который изначально был нужен лишь для экспорта функций. Но на деле оказалось что всю бизнес-логику тоже намного проще писать явными сигнатурами, потому что тогда компилятору меньше работы, и если он что-то понял не так, то он сообщает об этом максимально близко к источнику проблемы.
S>Ну, вот это и мешает писать нормально.
А, то есть значок | грубо говоря в зависимости от операндов делает либо
— множество из двух элементов
— множество с добавленным элементов
— объединение множеств
А там может оказаться алгебраическая сумма int64_t | long long int? Или это таки один тип?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, T4r4sB, Вы писали:
TB>Я разрабатывал язык в котором по умолчанию любая функция шаблонная, которая по контексту определяет какие у неё параметры, сколько их и что она возвращает. Так вот это полный треш когда изменение в одной функции по цепочке приводило к тому, что компилятор что-то своё додумал в другом файле проекта и по итогу в лучшем случае ошибка компиляции, в худшем просто хрень выводится. TB>А ещё малозаметные проблемы когда компилятор из-за незначительных нюансов для двух наборов параметров навыводил разные перегрузки (ну типа в одном случае он решил передавать копию потому что объект временный, а в другом ссылку), и в бинарнике появляются две почти одинаковые функции. Раздувание бинарника на пустом места.
Да, такая штука очень плохо дружит с перегрузками. Во flow9 перегрузок нет, что ограничивает некоторые сценарии.
TB>В языке был механизм явно указывать сигнатуру, который изначально был нужен лишь для экспорта функций. Но на деле оказалось что всю бизнес-логику тоже намного проще писать явными сигнатурами, потому что тогда компилятору меньше работы, и если он что-то понял не так, то он сообщает об этом максимально близко к источнику проблемы.
Ну вот видите — это и есть примерно то, что нужно.
S>>Ну, вот это и мешает писать нормально.
TB>Как-то так. Объединять типы надо вручную TB>Проблема не нова TB>https://www.reddit.com/r/rust/comments/jwnsp4/anonymous_sum_types_for_rust_error_handling/
Да, там всё верно описано.
S>>Одно и то же. И B|(C|A) — тоже.
TB>А, то есть значок | грубо говоря в зависимости от операндов делает либо TB>- множество из двух элементов TB>- множество с добавленным элементов TB>- объединение множеств
Он всегда делает объединение множеств. Просто множество может состоять из одного элемента. TB>А там может оказаться алгебраическая сумма int64_t | long long int? Или это таки один тип?
В тайпскрипте таких типов нет, там есть просто number. Зато можно объединять что угодно.
Во flow9 примитивные типы есть, но они не могут быть частью алгебры типов. То есть объединить int и float нельзя.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Result objects - все-таки победили Exceptions?
Здравствуйте, Sinclair, Вы писали:
S>Да, такая штука очень плохо дружит с перегрузками. Во flow9 перегрузок нет, что ограничивает некоторые сценарии.
Это как, любой шаблон инстанциируется только 1 раз что ли?
S>Он всегда делает объединение множеств. Просто множество может состоять из одного элемента.
Тогда получается, что элемент и множество из этого элемента — это одно и то же. Но это не так.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[23]: Result objects - все-таки победили Exceptions?
Здравствуйте, T4r4sB, Вы писали: TB>Это как, любой шаблон инстанциируется только 1 раз что ли?
Нет, просто перегрузок нет. Не получится объявить
sum(x: int, y: int) -> int
а потом
sum(x: float, y: float)->float
S>>Он всегда делает объединение множеств. Просто множество может состоять из одного элемента. TB>Тогда получается, что элемент и множество из этого элемента — это одно и то же. Но это не так.
Не обязательно. Считайте, что "отдельных" типов не существует — только множества, состоящие из одного типа.
То есть ровно везде, где вы пишете int, компилятор видит { int }.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[24]: Result objects - все-таки победили Exceptions?
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, T4r4sB, Вы писали: TB>>Это как, любой шаблон инстанциируется только 1 раз что ли? S>Нет, просто перегрузок нет. Не получится объявить
А, ты про такие.
Я говорю про инстансы одного шаблона. Из-за того что функция шаблонная, компилятор генерит 100500 ненужных инстансов, которые по бинарному коду почти не различаются.
Ну а с неизвестными ошибками пришлось бы все функции объявлять шаблонными. Или там можно сказать "сигнатура жёстко именно такая только тип ошмбки сам выведи"?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[25]: Result objects - все-таки победили Exceptions?
Здравствуйте, T4r4sB, Вы писали:
TB>А, ты про такие. TB>Я говорю про инстансы одного шаблона. Из-за того что функция шаблонная, компилятор генерит 100500 ненужных инстансов, которые по бинарному коду почти не различаются.
Я под капот flow9 не лазил, но, думаю, там просто все шаблоны склеиваются.
Там можно не указывать типы аргументов — тогда они выведутся из использования. Но нельзя будет вывести два разных типа.
То есть вот такое вот выдаст ошибку:
Не получается типизировать аргумент foo.
Но можно сделать шаблонную функцию:
foo(a: ?)
{
Some(a);
}
Эта функция для аргумента любого типа x возвращает экземпляр типа Some<x>.
TB>Ну а с неизвестными ошибками пришлось бы все функции объявлять шаблонными. Или там можно сказать "сигнатура жёстко именно такая только тип ошмбки сам выведи"?
Во flow9 — можно. Потому что можно зафиксировать тип у аргументов функции, а результат не указывать.
Например так:
Результат тут выведется сам, это будет Some<string>.
Но там есть несколько сложностей с т.з. компилятора — в частности, я только что сломал ему типизацию, пытаясь написать для вас пример
Вот такой код приводит к рантайм фейлу (исключений там нет, так что программа сразу выпадает):
Тип результата у foo выводится в ? (а должен приводить к compile-time error), по факту в t1 возвращается строка "42", к которой потом не удаётся применить оператор +.
Но вы, наверное, имели в виду что-то другое — чтобы функция могла возвращать "int или любую ошибку", но не "int или string или ошибку".
Во flow9 такого нет (в нём вообще алгебра типов не особо могучая); а в TS — сходу не могу сказать. Я его всего полтора года изучаю, и пока что пришёл к выводу, что на любой код есть ещё более идиоматический способ записи
На первый взгляд такой возможности нет (потому что синтаксичкески в языке можно либо указать тип, либо не указать); но я не удивлюсь, если там окажется возможность как-то хитровывернуто это записать.
Ну, вот паттерн-матчинга в компиляторе TS нету, но в ts-pattern мало того, что тип аргумента сокращается по мере прохождения паттернов, так там и .exhaustive() есть.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[26]: Result objects - все-таки победили Exceptions?
Здравствуйте, Sinclair, Вы писали:
S>Там можно не указывать типы аргументов — тогда они выведутся из использования. Но нельзя будет вывести два разных типа.
А, я понял. Тогда одна из проблем снимается. Но остается "почему тут вывелся не тот тип который как мне казалось тут должен появиться". Разумеется такое бывает и в Расте и в С++, но только у тех кто злоупотребляет шаблонами.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте