Re[2]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 19:22
Оценка:
Здравствуйте, ononim, Вы писали:
O>И тогда код с сериализацией в файл будет выглядеть так:
O>
O>FileObject fo("/foo/bar", "w");
O>fo.WriteString("foo");
O>fo.WriteString("bar");
O>fo.WriteInteger(123);
O>fo.Flush();
O>if (fo.GetError()) ShowMessage(fo.GetError(true)->Text);
O>

Я так понимаю, если промежуточная операция не удалась, то весь остальной код продолжает работать как ни в чём не бывало, и лишь операции с тем же FacilityID перестают выполняться? Звучит сомнительно
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[15]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 13.01.25 20:32
Оценка: 1 (1)
·>Я не очень понимаю. Как на ерланге написать код вроде

Язык позволяет и 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, да умноженное на "копроративное форматирование кода", получается швах.
Отредактировано 13.01.2025 22:05 SkyDance . Предыдущая версия .
Re[16]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 21:10
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD> {error, {right, parse_failed, Reason}}

SD> {error, left_parse_failed, Reason}

А почему по-разному написал?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[3]: Result objects - все-таки победили Exceptions?
От: ononim  
Дата: 13.01.25 21:42
Оценка:
O>>И тогда код с сериализацией в файл будет выглядеть так:
O>>
O>>FileObject fo("/foo/bar", "w");
O>>fo.WriteString("foo");
O>>fo.WriteString("bar");
O>>fo.WriteInteger(123);
O>>fo.Flush();
O>>if (fo.GetError()) ShowMessage(fo.GetError(true)->Text);
O>>

TB>Я так понимаю, если промежуточная операция не удалась, то весь остальной код продолжает работать как ни в чём не бывало, и лишь операции с тем же FacilityID перестают выполняться? Звучит сомнительно
Почему сомнительно? Зачем проверять что операция не удалась на каждом ее этапе, если в принципе ничего не измениться если я это проверю только тогда, когда мне нужно определиться с ее финальным статусом? В большинстве случаев это вполне допустимо. В данном случае — ну не удалось сохранить файл, покажем юзеру мессагу он ткнет ретрай и сохраним еще раз, или отмену. Что даст осознание факта неудачи прям в тот самый момент? Даст лишь +100500 строк проверки на код ошибки если у нас сериализация здорового объекта. Такая вот крайность. Исключения — это можно сказать противоположная крайность. Истина — она всегда посередине.
Как много веселых ребят, и все делают велосипед...
Re[17]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 13.01.25 22:06
Оценка:
SD>> {error, {right, parse_failed, Reason}}
SD>> {error, left_parse_failed, Reason}
TB>А почему по-разному написал?

А потому что error-prone, блин.

Исправил, сейчас написано одинаково.
Re[4]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 22:13
Оценка:
Здравствуйте, ononim, Вы писали:

O>Почему сомнительно?


Потому что процесс все равно продолжается. С битыми данными, которые мы передаем в функцию не знающую ни про какой FacilityID.
Ну либо весь язык с самого начала дизайнить чтоб он требовал задавать множество FacilityID для каждой операции. Сдается мне что это будет еще и тормознуто — мы проверяем код ошибки не после функции а в начале каждой следующей функции
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[18]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 22:15
Оценка:
Здравствуйте, 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?
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.01.25 02:42
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>А что такое x, y для начала? Тогда я смогу описать этот код на Расте.

Ровно тот же пример, на который я ссылался в предыдущем посте.
TB>В самом простом случае код такой:
TB>
TB>fn bar() -> Result<i32, BarError> { ... }
TB>fn baz() -> Result<i32, BazError> { ... }

TB>fn foo() -> Result<i32, Box<dyn Error>> { 
TB>  Ok(bar()? + baz()?)
TB>}
TB>

Выглядит почти прекрасно.
А без явного указания типа результата никак? И есть ли возможность вернуть Result<i32, BarError|BazError>?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[18]: Result objects - все-таки победили Exceptions?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.01.25 09:23
Оценка: +1
Здравствуйте, SkyDance, Вы писали:

SD>>> {error, {right, parse_failed, Reason}}

SD>>> {error, left_parse_failed, Reason}
TB>>А почему по-разному написал?

SD>А потому что error-prone, блин.


SD>Исправил, сейчас написано одинаково.


Эээ

{error, {right, parse_failed, Reason}}
{error, {left_parse_failed, Reason}}


спешишь слишком торопясь...

Я бы ожидал {error, {parse_failed, left, Reason}}, {error, {parse_failed, right, Reason}} — ещё и атомы переставить.
The God is real, unless declared integer.
Re[18]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 14.01.25 09:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А без явного указания типа результата никак?


Можно описать тип результата основываясь на типах аргументов.
А сказать "сам выведи по содержимому функции" — нельзя. И это очень хорошо потому что я знаю каково это когда поменял одну строчку в одной функции и из-за этого поползли изменения сигнатуры по всей кодовой базы, и иногда там где вообще не хочется, и не всегда понятно почему.
Один из принципов Раста — весь умный анализ сидит внутри функции. При этом вся инфа для межфункционального анализа зашита в явно прописанной сигнатуре функции.

S> И есть ли возможность вернуть Result<i32, BarError|BazError>?


Можно только если ты сам опишешь нужный таггед юнион и вручную пропишешь мапперы в него. С телефона не напишу

Кстати а A|B|C , (A|B)|C и A|(B|C) это одно и то же или разная фигня?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[19]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.01.25 09:31
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Можно описать тип результата основываясь на типах аргументов.

TB>А сказать "сам выведи по содержимому функции" — нельзя. И это очень хорошо потому что я знаю каково это когда поменял одну строчку в одной функции и из-за этого поползли изменения сигнатуры по всей кодовой базы, и иногда там где вообще не хочется, и не всегда понятно почему.
Не вижу проблемы, если указание типов опционально. То есть хочешь — и всё едет. Не хочешь — пишешь явно "здесь должно быть вот так", и дальше этой строчки изменения не поползут.

TB>Можно только если ты сам опишешь нужный таггед юнион и вручную пропишешь мапперы в него. С телефона не напишу

Ну, вот это и мешает писать нормально.

TB>Кстати а A|B|C , (A|B)|C и A|(B|C) это одно и то же или разная фигня?

Одно и то же. И B|(C|A) — тоже.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Result objects - все-таки победили Exceptions?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 14.01.25 09:36
Оценка:
Здравствуйте, ononim, Вы писали:

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

O>Я думаю концепция GetLastError()/errno недооценена
O>Но ее надо чутка расширить, параметром facility.И API сделать таким:
O>
O>void SetError(FacilityID facility, ErrorObject error);
O>ErrorObject *GetError(FacilityID facility, bool reset = false);
O>


Я нифига не понял, как это предполагается использоваться.
Что будет, если код раньше поставил ошибку, а код позже решил обнулить ошибку ради проверки результата другого кода?
(см. man strtol, как это выглядит на практике. и он не один такой)

В случае исключений или объектов результатов нет проблемы с перекрытием прошлых ошибок, если нет явного (легче отловить) игнорирования ошибки. А тут как это обеспечивается?
The God is real, unless declared integer.
Re[3]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 14.01.25 14:55
Оценка:
Здравствуйте, netch80, Вы писали:



N>Я нифига не понял, как это предполагается использоваться.

N>Что будет, если код раньше поставил ошибку, а код позже решил обнулить ошибку ради проверки результата другого кода?

Видимо разный FalicittID нужен для этого
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[20]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 14.01.25 18:51
Оценка: 3 (1)
S>Не вижу проблемы, если указание типов опционально. То есть хочешь — и всё едет. Не хочешь — пишешь явно "здесь должно быть вот так", и дальше этой строчки изменения не поползут.

Я разрабатывал язык в котором по умолчанию любая функция шаблонная, которая по контексту определяет какие у неё параметры, сколько их и что она возвращает. Так вот это полный треш когда изменение в одной функции по цепочке приводило к тому, что компилятор что-то своё додумал в другом файле проекта и по итогу в лучшем случае ошибка компиляции, в худшем просто хрень выводится.
А ещё малозаметные проблемы когда компилятор из-за незначительных нюансов для двух наборов параметров навыводил разные перегрузки (ну типа в одном случае он решил передавать копию потому что объект временный, а в другом ссылку), и в бинарнике появляются две почти одинаковые функции. Раздувание бинарника на пустом места.
В языке был механизм явно указывать сигнатуру, который изначально был нужен лишь для экспорта функций. Но на деле оказалось что всю бизнес-логику тоже намного проще писать явными сигнатурами, потому что тогда компилятору меньше работы, и если он что-то понял не так, то он сообщает об этом максимально близко к источнику проблемы.

S>Ну, вот это и мешает писать нормально.


Как-то так. Объединять типы надо вручную
struct BarError {}

fn bar() -> Result<i32, BarError> {
    Ok(10)
}

struct BazError {}

enum BarOrBazError {
    BarError(BarError),
    BazError(BazError),
}

impl From<BarError> for BarOrBazError {
    fn from(bar_error: BarError) -> Self {
        Self::BarError(bar_error)
    }
}

impl From<BazError> for BarOrBazError {
    fn from(baz_error: BazError) -> Self {
        Self::BazError(baz_error)
    }
}

fn baz() -> Result<i32, BazError> {
    Ok(20)
}

fn foo() -> Result<i32, BarOrBazError> {
    Ok(bar()? + baz()?)
}


Проблема не нова
https://www.reddit.com/r/rust/comments/jwnsp4/anonymous_sum_types_for_rust_error_handling/

S>Одно и то же. И B|(C|A) — тоже.


А, то есть значок | грубо говоря в зависимости от операндов делает либо
— множество из двух элементов
— множество с добавленным элементов
— объединение множеств
А там может оказаться алгебраическая сумма int64_t | long long int? Или это таки один тип?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Отредактировано 14.01.2025 19:26 T4r4sB . Предыдущая версия .
Re[21]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.01.25 04:22
Оценка:
Здравствуйте, 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?
От: T4r4sB Россия  
Дата: 15.01.25 06:49
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


Это как, любой шаблон инстанциируется только 1 раз что ли?

S>Он всегда делает объединение множеств. Просто множество может состоять из одного элемента.


Тогда получается, что элемент и множество из этого элемента — это одно и то же. Но это не так.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[23]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.01.25 09:12
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>Это как, любой шаблон инстанциируется только 1 раз что ли?
Нет, просто перегрузок нет. Не получится объявить
sum(x: int, y: int) -> int

а потом
sum(x: float, y: float)->float

S>>Он всегда делает объединение множеств. Просто множество может состоять из одного элемента.
TB>Тогда получается, что элемент и множество из этого элемента — это одно и то же. Но это не так.
Не обязательно. Считайте, что "отдельных" типов не существует — только множества, состоящие из одного типа.
То есть ровно везде, где вы пишете int, компилятор видит { int }.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[24]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 15.01.25 10:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, T4r4sB, Вы писали:

TB>>Это как, любой шаблон инстанциируется только 1 раз что ли?
S>Нет, просто перегрузок нет. Не получится объявить

А, ты про такие.
Я говорю про инстансы одного шаблона. Из-за того что функция шаблонная, компилятор генерит 100500 ненужных инстансов, которые по бинарному коду почти не различаются.

Ну а с неизвестными ошибками пришлось бы все функции объявлять шаблонными. Или там можно сказать "сигнатура жёстко именно такая только тип ошмбки сам выведи"?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[25]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 15.01.25 11:16
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>А, ты про такие.

TB>Я говорю про инстансы одного шаблона. Из-за того что функция шаблонная, компилятор генерит 100500 ненужных инстансов, которые по бинарному коду почти не различаются.
Я под капот flow9 не лазил, но, думаю, там просто все шаблоны склеиваются.

Там можно не указывать типы аргументов — тогда они выведутся из использования. Но нельзя будет вывести два разных типа.
То есть вот такое вот выдаст ошибку:
foo(a) { false; }

main()
{
  foo(42) && foo(hello);
}

Не получается типизировать аргумент foo.
Но можно сделать шаблонную функцию:
foo(a: ?)
{
  Some(a);
}

Эта функция для аргумента любого типа x возвращает экземпляр типа Some<x>.

TB>Ну а с неизвестными ошибками пришлось бы все функции объявлять шаблонными. Или там можно сказать "сигнатура жёстко именно такая только тип ошмбки сам выведи"?

Во flow9 — можно. Потому что можно зафиксировать тип у аргументов функции, а результат не указывать.
Например так:
foo(a: string, b: bool)
{
  if(b)
    Some(a)
  else
    None()
}

Результат тут выведется сам, это будет Some<string>.
Но там есть несколько сложностей с т.з. компилятора — в частности, я только что сломал ему типизацию, пытаясь написать для вас пример

Вот такой код приводит к рантайм фейлу (исключений там нет, так что программа сразу выпадает):
foo(a: (int)->?, b: (int)->??, c)
{
    if(c)
        a(42)
    else
        b(42);
}

main()
{
    t1 = foo(\x->x+1, \x->i2s(x), false);
    t2 = t1 + 2;
}

Тип результата у foo выводится в ? (а должен приводить к compile-time error), по факту в t1 возвращается строка "42", к которой потом не удаётся применить оператор +.


Но вы, наверное, имели в виду что-то другое — чтобы функция могла возвращать "int или любую ошибку", но не "int или string или ошибку".
Во flow9 такого нет (в нём вообще алгебра типов не особо могучая); а в TS — сходу не могу сказать. Я его всего полтора года изучаю, и пока что пришёл к выводу, что на любой код есть ещё более идиоматический способ записи
На первый взгляд такой возможности нет (потому что синтаксичкески в языке можно либо указать тип, либо не указать); но я не удивлюсь, если там окажется возможность как-то хитровывернуто это записать.
Ну, вот паттерн-матчинга в компиляторе TS нету, но в ts-pattern мало того, что тип аргумента сокращается по мере прохождения паттернов, так там и .exhaustive() есть.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[26]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 15.01.25 11:31
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Там можно не указывать типы аргументов — тогда они выведутся из использования. Но нельзя будет вывести два разных типа.


А, я понял. Тогда одна из проблем снимается. Но остается "почему тут вывелся не тот тип который как мне казалось тут должен появиться". Разумеется такое бывает и в Расте и в С++, но только у тех кто злоупотребляет шаблонами.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.