Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 15:40
Оценка: +1
Ну вот рекомендации в новомодных языках: https://docs.flutter.dev/app-architecture/design-patterns/result

— все-таки топят за Result objects для бизнес-логики.

А ведь это всю цепочку поддерживать. Как-то много лишних букв добавляется.

Но! Exception как бы не определены в контрактах. Если ResultObject — это контракт, его понятно что нужно проверить и компилятор даже контролирует эту проверку. А Exception разве что в документации описан, которую могут не читать.

А ведь было же хорошее решение — т.н. проверяемые исключения в Java. Когда компилятор требовал проверки того или иного исключения, но так же была возможно обернуть в RuntimeException, если оно утратило смысл бизнес-логики или ожидаемого

Но, как оказалось, народ идеи не понял. Так и вернулись к понятным дебилоидам кодам возврата, т.к. проверяемые исключения осилить не смогли. Так же и в Kotlin их решили не делать.
=сначала спроси у GPT=
Отредактировано 05.01.2025 15:42 Shmj . Предыдущая версия .
Re: Result objects - все-таки победили Exceptions?
От: SomeOne_TT  
Дата: 05.01.25 17:00
Оценка: -2 :))
Здравствуйте, Shmj, Вы писали:

S>Ну вот рекомендации в новомодных языках: https://docs.flutter.dev/app-architecture/design-patterns/result


S>- все-таки топят за Result objects для бизнес-логики.


S>А ведь это всю цепочку поддерживать. Как-то много лишних букв добавляется.


Современные result objects позволяют не описывать все варианты проблем по всей цепочке вызовов, а хранить их "динамически", восстанавливая
тип ошибки в месте обработки. В расте, например, весьма популярны Result<(), Box<dyn Error>> и anyhow::Result<()>
Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 17:14
Оценка:
Здравствуйте, SomeOne_TT, Вы писали:

SO_>Современные result objects позволяют не описывать все варианты проблем по всей цепочке вызовов, а хранить их "динамически", восстанавливая

SO_>тип ошибки в месте обработки. В расте, например, весьма популярны Result<(), Box<dyn Error>> и anyhow::Result<()>

А что не современные этого не позволяли? В чем разница? Что принципиально нового появилось?

Коды возврата использовали еще деды. Основная претензия была — из можно было забыть проверить.

С проверяемыми исключениями вам доводилось сталкиваться, как в Java?
=сначала спроси у GPT=
Re: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 05.01.25 17:49
Оценка: 2 (2) +3
Моду на коды ошибок вместо исключений ввели разработчики Rust, причём только потому, что не смогли совместить исключения и Borrow Checker. Других причин не было. Вся чушь, которую написали после: про скорость, или там надёжность с безопасностью — чистая идеологическая хрень. Написать "мы облажались, терпите" означало растерять фан-базу.

Деды программирования наелись кодов ошибок по полной задолго до нас. И, имея в тысячи раз меньше памяти и медленнее процессора, эти деды таки сделали исключения. Что ждёт rust и другие языки — наворачивание обёрток и макросов для "удобного проброса" кодов ошибок и стектрейсов наверх. И в итоге они получат те же исключения. Есть шанс, что, при нормальном метапрограммировании, получится даже круче и фичастее чем то, что сейчас разработчики компиляторов хардкодят. Например, декларативно задавать, какие операции перезапускаемые, или в конфигах описывать какие исключения обязательно должны пойти в лог. Или автоматическое сохранение и передача всяких Trace-Id.

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

S>А ведь было же хорошее решение — т.н. проверяемые исключения в Java. Когда компилятор требовал проверки того или иного исключения, но так же была возможно обернуть в RuntimeException, если оно утратило смысл бизнес-логики или ожидаемого

Хельсберг очень подробно когда-то объяснял, почему эту фичу в C# не потащили. Кмк, что реально нужно — возможность указать no throw, и throws(список). Чтобы те, кому нужна железная уверенность что всё перехватывается, могли желаемую надёжность получить. А те, кому достаточно вывести на экран сообщение "коннекта к базе нет", могли писать db.Connect() и радоваться что единственный try/catch в main решает все их проблемы.
Re: Result objects - все-таки победили Exceptions?
От: Alekzander Россия  
Дата: 05.01.25 17:57
Оценка: +2
Здравствуйте, Shmj, Вы писали:

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


Дебилоиды это те, кто не понимает, что каждому инструменту своё применение.

try
{
   const data = LoadData();
   const processedData = ProcessData(data);
   SaveData(processedData);
}
catch
{
    log.Error("Всё пропало");
}


Допустим, ProcessData() это дорогая операция (рендер на 12 часов для десктопной программы или облачные вычисления за 1000$ для серверной). А при попытке SaveData() происходит облом. Если это серверная программа, то база не соединяется, а если десктопная, то файловая операция не проходит. В этом случае надо вернуть код ошибки, чтобы алгоритм можно было переписать так (серверная программа):

while (tryCount++ < 10 && NotOkay == SaveData(processedData))
{
    await ThisFrameworkSleep(1000);
}


Или так (десктопная):

while (NotOkay == SaveData(processedData) && Yes == AskUser("Попытаться обратно?"));


А если за каким-то хером внутри SaveData() оказывается, что processedData внезапно null или InvalidObject, то тогда у нас ИСКЛЮЧИТЕЛЬНАЯ ситуация. Весь алгоритм уже пошёл по звезде, и проверять результат бесполезно. В такой исключительной ситуации нужно выбрасывать исключение. Поэтому они и называются исключениями.

Единственная причина полностью отказаться от исключений — технические соображения. Лично я считаю, что в отсутствие виртуальной машины вреда от исключений больше, чем пользы. Хотя бы потому, что если у нас unmanaged OS и проект состоит из разных модулей, написанных на разных языках с разными стандартными классами исключений, их заколебёшься приводить к одному знаменателю. Да и нахрен не всралось исключение, если неперехваченное оно не даёт при желании хромать дальше с восьмой цифры (как бывает без ВМ).

Но поскольку дебилоидов слишком много, то и общаются они на уровне "а давайте всегда выкидывать исключения ДЛЯ КОНСИСТЕНТНОСТИ". Жопу бы им начать вытирать зубной щёткой. Для консистентности.
Отредактировано 05.01.2025 18:11 Alekzander . Предыдущая версия . Еще …
Отредактировано 05.01.2025 18:00 Alekzander . Предыдущая версия .
Re: Result objects - все-таки победили Exceptions?
От: Privalov  
Дата: 05.01.25 18:32
Оценка:
Здравствуйте, Shmj, Вы писали:

S>А ведь было же хорошее решение — т.н. проверяемые исключения в Java. Когда компилятор требовал проверки того или иного исключения, но так же была возможно обернуть в RuntimeException, если оно утратило смысл бизнес-логики или ожидаемого


Я как-то показывал типичный код на Java
void MyCoolMethod(int a, int b) throws Exception
{
   int p;
   
   try
   {
       // 100500 строк кода
   }
   catch (Exception ex)
   {
   }
}


Лень разработчикам указывать все исключения, которые могут прилететь. Я в своё время запарился с таким кодом работать.
Re[2]: Result objects - все-таки победили Exceptions?
От: Alekzander Россия  
Дата: 05.01.25 19:03
Оценка:
Здравствуйте, Privalov, Вы писали:

P>Лень разработчикам указывать все исключения, которые могут прилететь. Я в своё время запарился с таким кодом работать.


...Pokémon exception handling after the show's catchphrase "Gotta Catch ‘Em All!"

Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 19:10
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>Хельсберг очень подробно когда-то объяснял, почему эту фичу в C# не потащили. Кмк, что реально нужно — возможность указать no throw, и throws(список).


Так в Java есть не проверяемые — это наследники RuntimeException.
=сначала спроси у GPT=
Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 19:14
Оценка:
Здравствуйте, Alekzander, Вы писали:

A>
A>try
A>{
A>   const data = LoadData();
A>   const processedData = ProcessData(data);
A>   SaveData(processedData);
A>}
A>catch
A>{
A>    log.Error("Всё пропало");
A>}
A>


Так нельзя писать. Во-первых, всегда нужно указывать типы исключений. Но беда в том, что вы обычно не думаете об этом — у вас нет четкой картинки в голове какие исключения может вызвать функция. Т.е. вы теряете контроль. А у меня всегда 100% я знаю какие исключения возможны, я всегда это держу в голове для каждой функции.

Далее. Исключения нужно отлавливать для каждого шага отдельно — т.е. не все в куче — а везде, на каждом из шагов где могут возникнуть исключения — нужно конкретные отлавливать и обрабатывать, если возмжно.
=сначала спроси у GPT=
Re[2]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 19:15
Оценка: 1 (1) +1
Здравствуйте, Privalov, Вы писали:

P>Лень разработчикам указывать все исключения, которые могут прилететь. Я в своё время запарился с таким кодом работать.


Ну так испортить можно любую идею.
=сначала спроси у GPT=
Re[3]: Result objects - все-таки победили Exceptions?
От: hi_octane Беларусь  
Дата: 05.01.25 20:05
Оценка: +1
S>Так в Java есть не проверяемые — это наследники RuntimeException.
Делить исключения на проверяемые и нет — ошибка. В разных частях проекта даже одинаковые исключения могут быть как проверяемые так и нет.
Re[3]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 05.01.25 20:09
Оценка: +2 -1
Здравствуйте, Shmj, Вы писали:


S>Далее. Исключения нужно отлавливать для каждого шага отдельно


То есть
var (result, error) = some_func();
if (error)
  // kak-to obrabotat
  return (_, error);

на каждый чих — это зашквар


try {
  var result = some_func();
} catch (error) {
  // kak-to obrabotat
  throw error;
}

на каждый чих — это бест практис
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[2]: Result objects - все-таки победили Exceptions?
От: ArtDenis Россия  
Дата: 05.01.25 20:11
Оценка:
Здравствуйте, hi_octane, Вы писали:

_>Моду на коды ошибок вместо исключений ввели разработчики Rust


Коды ошибок в расте? Интересно...
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[4]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 20:15
Оценка:
Здравствуйте, hi_octane, Вы писали:

S>>Так в Java есть не проверяемые — это наследники RuntimeException.

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

Проверяемые — это часть контакта. Т.е. они хороши для бизнес-логики или основной логической ошибки утилиты.

В точке где исключение меняет акцент — переходит из проверяемого в непроверяемого — оборачиваете в RuntimeException.

Просто многие не разобрались с парадигмой и она стала только во вред.
=сначала спроси у GPT=
Re[4]: Result objects - все-таки победили Exceptions?
От: Shmj Ниоткуда  
Дата: 05.01.25 20:17
Оценка: +1
Здравствуйте, T4r4sB, Вы писали:

TB>То есть

TB>
TB>var (result, error) = some_func();
TB>if (error)
TB>  // kak-to obrabotat
TB>  return (_, error);
TB>

TB>на каждый чих — это зашквар


TB>
TB>try {
TB>  var result = some_func();
TB>} catch (error) {
TB>  // kak-to obrabotat
TB>  throw error;
TB>}

TB>на каждый чих — это бест практис

Если нет обработки — то просто возвращаете результат. А в случае с кодами ошибок — вы возвращаете обертку. Постоянно перед глазами бойлерплейт код этой обертки.
=сначала спроси у GPT=
Re[2]: Result objects - все-таки победили Exceptions?
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.01.25 21:15
Оценка: +1
Здравствуйте, hi_octane, Вы писали:

_>Моду на коды ошибок вместо исключений ввели разработчики Rust, причём только потому, что не смогли совместить исключения и Borrow Checker. Других причин не было. Вся чушь, которую написали после: про скорость, или там надёжность с безопасностью — чистая идеологическая хрень. Написать "мы облажались, терпите" означало растерять фан-базу.


Мне кажется, это прямое влияние Go. А там оно из Alef-а, который прямой предшественник Go.

И сделали они это так, потому, что им исключения не нравятся по сути своей. Тем, что разрывают поток исполнения программы, делая его нелинейным, а потому трудным для понимания человеком.

_>Деды программирования наелись кодов ошибок по полной задолго до нас.


Go/Alef — это даже не деды, а прадеды. Ричи, Томпсон, Пайк...
Re[3]: Result objects - все-таки победили Exceptions?
От: SomeOne_TT  
Дата: 05.01.25 22:26
Оценка: +1 -2 :))
Здравствуйте, Shmj, Вы писали:

S>А что не современные этого не позволяли? В чем разница? Что принципиально нового появилось?


С Result принципиален сахарок, который позволяет не делать мучительные "проверки" в стиле го на каждый чих, а
писать что-нибудь вроде
var value = Add(2,3)?


которое компилятором разворачивается в
Result<int> result = Add(2,3);
if (result.is_err())
     return result
T value = result.ok();


Надеюсь, впрочем, что и в современном го такое сделали или сделают.


S>С проверяемыми исключениями вам доводилось сталкиваться, как в Java?


Ясен пень. Плохо отношусь. Слишком много труда и слишком мало выхлопа.
Отредактировано 05.01.2025 22:27 SomeOne_TT . Предыдущая версия .
Re[2]: Result objects - все-таки победили Exceptions?
От: johny5 Новая Зеландия
Дата: 06.01.25 04:52
Оценка: +1 -2
Здравствуйте, hi_octane, Вы писали:

_>Моду на коды ошибок вместо исключений ввели разработчики Rust, причём только потому, что не смогли совместить исключения и Borrow Checker. Других причин не было. Вся чушь, которую написали после: про скорость, или там надёжность с безопасностью — чистая идеологическая хрень. Написать "мы облажались, терпите" означало растерять фан-базу.


Ох, держите меня семеро. Это ж надо так на Раст наехать. Вы на нём не пишете? Может стоит сначала пописать/попробовать?
В Расте всё это обёрнуто так, что теперь это растаскивают во все языки, включая С++. Означает ли это что Растовцы облажались? Или может наоборот, нащупали что то в пыльной коробке?

Сахарок с вопросительным знаком отлично ложится в синтаксис, что это даёт:
  1. Вид ошибки прописан в прототипе функции
  2. Явно прокидываем ошибку, а не "я не знаю кинет оно или нет" что частенько приводит к сюрпризам в проде и перечитыванию доков. Просто нужно написать "?". При этом задумываешься. Ровно то же самое когда разворачиваешь optional<> (который тоже обретает лютую популярность в С++): всё что нужно сделать, написать "*", но задумываешься, ведь там объекта может и не быть.
  3. В коде явно видно как ошибка обрабатывается.
  4. Всегда есть опция обработать ошибку на месте, причём без специального очень шумного try/catch/finally/throw синтаксиса а просто получить тип и сматчить его как угодно — часто в одну строку (map, map_or_else, and, or, and_then, ...). Работа с обычной переменной.
Re[2]: Result objects - все-таки победили Exceptions?
От: Pavel Dvorkin Россия  
Дата: 06.01.25 06:13
Оценка: :)
Здравствуйте, Privalov, Вы писали:

P>Лень разработчикам указывать все исключения, которые могут прилететь. Я в своё время запарился с таким кодом работать.


В современных IDE нет никакой проблемы, чтобы указать. IDEA просто не даст откомпилировать метод, в котором выбрасывается checked исключение, потребует либо поставить try-catch, либо добавить throw, и сама это сделает.
With best regards
Pavel Dvorkin
Re: Result objects - все-таки победили Exceptions?
От: Pavel Dvorkin Россия  
Дата: 06.01.25 06:21
Оценка: 1 (1) +3
Здравствуйте, Shmj, Вы писали:

S>Ну вот рекомендации в новомодных языках: https://docs.flutter.dev/app-architecture/design-patterns/result


S>- все-таки топят за Result objects для бизнес-логики.


S>А ведь это всю цепочку поддерживать. Как-то много лишних букв добавляется.


Что ни делай — результат один и тот же.
Если обрабатывать надо не в точке вызова метода, в котором ошибка, а выше, то так или иначе придется передавать эту ошибку выше. Если использовать исключения, код обеспечит компилятор, если коды ошибок — пиши сам.

Исключения, в общем, проще.
У них ИМХО только один недостаток — их нельзя игнорировать. Коды ошибок можно, если знаешь, что тут что с ошибкой, что без ошибки — все равно.

Я как-то писал на C# удаление 0-го элемента из listbox. Все было замечательно, пока не оказалось, что в листбоксе нет вообще элементов. Я об этом не подумал, так как на Win API если послать сообщение LB_DELETESTRING при нулевом количестве элементов, то, конечно, получишь LB_ERR, но его можно спокойно проигнорировать, так как все, что нужно — чтобы элемента не было. Был он — чтобы его не было, не было — тоже чтобы не было, а какой из этих 2 вариантов сработал — неважно. А на C# пришлось условие ставить.
With best regards
Pavel Dvorkin
Отредактировано 06.01.2025 6:22 Pavel Dvorkin . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.