Re[16]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 11.01.25 10:11
Оценка:
Здравствуйте, Sinclair, Вы писали:

_>>Это алгебраический тип, но при чём тут это?

S>То, что OOM могло бы быть одним из его слагаемых.

Вот аналог такого алгебраического типа на Java:

public class OOMDemo {

    public static void main(String[] args) {
        var expr = new If(new Mul(new Val(1), new Val(0)),
                new Add(new Mul(new Val(4), new Val(10)),
                        new Val(2)),
                new Mul(new Val(2), new Add(new Val(7), new Val(14))));

        System.out.println(expr.eval()); // 42
    }
}

sealed interface Expr {

    int eval();
}

record Val(int value) implements Expr {

    public int eval() {
        return value;
    }
}

record Add(Expr x, Expr y) implements Expr {

    public int eval() {
        return x.eval() + y.eval();
    }
}

record Mul(Expr x, Expr y) implements Expr {

    public int eval() {
        return x.eval() * y.eval();
    }
}

record If(Expr cond, Expr thenCase, Expr elseCase) implements Expr {

    public int eval() {
        if (cond.eval() == 0) {
            return thenCase.eval();
        } else {
            return elseCase.eval();
        }
    }
}

record OOM() implements Expr {

    public int eval() {
        throw new OutOfMemoryError("OOM");
    }
}


Добавил OOM в качестве слагаемого. Что дальше?
Re[17]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.01.25 10:12
Оценка:
Здравствуйте, korvin_, Вы писали:
_>Это возможно только если под этот твой процесс сохранениня стейта уже была выделена вся необходимая для его работы память, что в языках с GC практически невозможно сделать
Это ещё почему? Сразу выделяем всё на старте.
_>соответственно твоё "сделать что-то полезное" точно так же наткнётся на ООМ.
Не обязательно.

_>Это в теории, на практике -- нет. Даже в С/C++ и прочих языках с "ручным" контролем памяти что-то сделать сложно. Поэтому приходит OOM-Killer

OOM-Killer — это следствие криворукой архитектуры линукса. Программы под винду пишутся на тех же С/С++, но при этом там нет (и не нужно) никакого OOM-Killer.

_>Поэтому на практике, что можно сделать: упасть. Гипервизор/оркерстратор перезапустит ноду/сервис/под/контейнер, а мы будем изучать логи, метрики, дампы, и по результату, либо добавим больше памяти, либо изменим параметры масштабирования, либо починим утечку, либо оптимизируем код.

Это очень, очень дорогостоящая процедура. Даже если удалось обойтись без обращения к разработчикам за починкой — всё равно: добавление памяти и перезапуск стоят дней простоя.
Не, я понимаю, что самый простой способ — это просто надеяться. Перезапускаем — может на этот раз карта ляжет так, что упавший процесс успеет отработать до того, как процесс с утечкой сожрёт всю память.
_>Так и что бы это дало? Как его использовать? Напиши пример.
Повторюсь: пример зависит от области деятельности и от того, где именно вылетел OOM. Если я получил OOM при создании шестибайтового объекта — да, скорее всего продолжать вообще смысла нет, и можно просто перезапускать весь контейнер или даже всю ноду. А если я пытаюсь выделить буфер размером в 100М, то шанс велик, что после вылета ООМ у меня всё ещё ~50М свободно. И можно заняться чем-то другим, не столь прожорливым к памяти. Или попробовать выделить буфер вдвое меньше. Ну, как пример — сортируем файл интов размером в 100гб. Понятно, что нужно делать сортировку слиянием; и понятно, что чем больше размер чанка, тем быстрее мы закончим. Но это не означает, что неудача выделения чанка в 10G — это катастрофа и нужно делать паник. Можно и 100мб кусками его сортировать — просто это займёт больше времени.

_>Так Optional -- это другой тип по отношению к int.

Ну, так и Expr | OOM — это другой тип по отношению к Expr.
_>Теперь, сам Optional же тоже требует памяти, значит Optional<Optional<T>>, и для него тоже нужна память, Optional<Optional<Optional<T>>>, и так далее.
Нет, зачем?
1. В хорошей системе типов Optional Optional == Optional.
2. Независимо от этого, new может всегда резервировать память под sizeof(optional)+sizeof(T), и возвращать none невзирая на то, кому из них не хватило места
4. Независимо от этого, вычислительная модель может быть построена на ref-типах (см. Java), и тогда Optional<T> вообще не требует никаких накладных расходов. Это ровно тот же "ссылка на T", только в отличие от "обычной" ссылки, эта может принимать значение none.

_>Так вот в Haskell/Ocaml и т.п. у нас нет new T, у нас сразу T, как 1, 2, true, false, "foo", "bar". Сразу значение.

Ну, это же иллюзия. Значения всё равно как-то конструируются. Иначе бы любая программа на Haskell была бы просто константой.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[16]: Result objects - все-таки победили Exceptions?
От: Pauel Беларусь http://blogs.rsdn.org/ikemefula
Дата: 11.01.25 12:34
Оценка:
Здравствуйте, Sinclair, Вы писали:

_>>Вот интересно, а что вы делаете в Java при получении OOM?

S>Смотря какой код у меня исполняется. Есть применения, где при получении OOM мне нужно не просто 500 Internal Error, а успеть сделать что-то полезное (например, сохранить стейт воркфлоу) перед тем, как умереть.

В .Net System.Drawing в свое время могло кидать OOM на ровном месте, при определенном сочетании параметров отрисовки. Например — отрисовка прерывистой линии когда расстояние меньше 1 пиксела.
Никакой нехватки памяти конечно же не было. Пришлось рендеринг примитивов завернуть в try-catch и просло логировать для дебага.
Re[18]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 11.01.25 12:50
Оценка:
Здравствуйте, Sinclair, Вы писали:

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

_>>Это возможно только если под этот твой процесс сохранениня стейта уже была выделена вся необходимая для его работы память, что в языках с GC практически невозможно сделать
S>Это ещё почему? Сразу выделяем всё на старте.

Это проблематично сделать, учитывая, что даже String (любая конкатенация == аллокация) и любой массив в Java -- Object. При этом нужно быть осторожным с использованием любого библиотечного кода, который может что-то аллоцировать, в том числе и неявно, и даже языковых конструкций, типа for (var x : array) { ... }, т.к. она приводит к аллокации итератора. В Хаскелле контроллировать аллокации ещё сложнее, явного new нет, любое значение может быть (и скорее всего будет) аллоцировано динамически, любой "вызов" функции может породить thunk. Удачи "выделить сразу всё на старте".

_>>соответственно твоё "сделать что-то полезное" точно так же наткнётся на ООМ.

S>Не обязательно.

Почти гарантированно.

_>>Это в теории, на практике -- нет. Даже в С/C++ и прочих языках с "ручным" контролем памяти что-то сделать сложно. Поэтому приходит OOM-Killer

S>OOM-Killer — это следствие криворукой архитектуры линукса. Программы под винду пишутся на тех же С/С++, но при этом там нет (и не нужно) никакого OOM-Killer.

Может и криворукой, но что делается в Windows в таких случаях?

_>>Поэтому на практике, что можно сделать: упасть. Гипервизор/оркерстратор перезапустит ноду/сервис/под/контейнер, а мы будем изучать логи, метрики, дампы, и по результату, либо добавим больше памяти, либо изменим параметры масштабирования, либо починим утечку, либо оптимизируем код.

S>Это очень, очень дорогостоящая процедура. Даже если удалось обойтись без обращения к разработчикам за починкой — всё равно: добавление памяти и перезапуск стоят дней простоя.

Какая процедура дорогостоящая? Перезапуск к Кубернетесе стоит секунды простоя. Добавление memory request/limits -- считанные минуты. При этом, это может коснуться только одного пода, остальные продолжат работать, если у них нет проблем. Тогда для пользователя никакого простоя не будет. Я уж не говорю о том, что ваш graceful shutdown при OOM это никак не решает. Памяти всё равно не хватило. О каких 24/7 вы говорили вообще?

S>Не, я понимаю, что самый простой способ — это просто надеяться. Перезапускаем — может на этот раз карта ляжет так, что упавший процесс успеет отработать до того, как процесс с утечкой сожрёт всю память.

_>>Так и что бы это дало? Как его использовать? Напиши пример.
S>Повторюсь: пример зависит от области деятельности и от того, где именно вылетел OOM. Если я получил OOM при создании шестибайтового объекта — да, скорее всего продолжать вообще смысла нет, и можно просто перезапускать весь контейнер или даже всю ноду. А если я пытаюсь выделить буфер размером в 100М, то шанс велик, что после вылета ООМ у меня всё ещё ~50М свободно. И можно заняться чем-то другим, не столь прожорливым к памяти. Или попробовать выделить буфер вдвое меньше. Ну, как пример — сортируем файл интов размером в 100гб. Понятно, что нужно делать сортировку слиянием; и понятно, что чем больше размер чанка, тем быстрее мы закончим. Но это не означает, что неудача выделения чанка в 10G — это катастрофа и нужно делать паник. Можно и 100мб кусками его сортировать — просто это займёт больше времени.

И долго вы будете гадать на кофейной OOM-гуще, сколько памяти вам надо? А если другой поток/процесс потребует столько же памяти? Если вы хотите буфер 10G, может, вам изначально стоило озаботиться наличием необходимой памяти?

_>>Так Optional -- это другой тип по отношению к int.

S>Ну, так и Expr | OOM — это другой тип по отношению к Expr.

Но вы предлагали засунуть OOM в Expr, т.е. сделать не другой тип.

_>>Теперь, сам Optional же тоже требует памяти, значит Optional<Optional<T>>, и для него тоже нужна память, Optional<Optional<Optional<T>>>, и так далее.

S>Нет, зачем?
S>1. В хорошей системе типов Optional Optional == Optional.

Нет, в любой нормальной системе типов Optional Optional -- это Optional Optional, также как List of List of T -- это List of List of T, а не List of T. А если они у вас автоматически схлопываются, у вас дерьмовая система типов.

S>2. Независимо от этого, new может всегда резервировать память под sizeof(optional)+sizeof(T), и возвращать none невзирая на то, кому из них не хватило места

S>4. Независимо от этого, вычислительная модель может быть построена на ref-типах (см. Java), и тогда Optional<T> вообще не требует никаких накладных расходов. Это ровно тот же "ссылка на T", только в отличие от "обычной" ссылки, эта может принимать значение none.

Она может не требовать, если компилятор особым образом компилирует Optional, а иначе это такой же тип-сумма, как любой другой со всеми вытекающими.

_>>Так вот в Haskell/Ocaml и т.п. у нас нет new T, у нас сразу T, как 1, 2, true, false, "foo", "bar". Сразу значение.

S>Ну, это же иллюзия. Значения всё равно как-то конструируются. Иначе бы любая программа на Haskell была бы просто константой.
Конструируются, конечно, но вы никак не можете знать, на стэке или в куче. Любой невинный Int может запросто быть аллоцирован в куче и никак не получится преаллоцироват его для работы в OOM-хэндлере.
Re[19]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.01.25 17:18
Оценка:
Здравствуйте, korvin_, Вы писали:

_>Может и криворукой, но что делается в Windows в таких случаях?

В Windows заранее резервируется нужное количество памяти. Если зарезервировать не удаётся, то процесс получает отказ. У Винды в принципе нет такой проблемы, как "я пообещала ста процессам по гигабайту рамы, а теперь они все за ней пришли, а на диске места нет".

_>Какая процедура дорогостоящая? Перезапуск к Кубернетесе стоит секунды простоя. Добавление memory request/limits -- считанные минуты. При этом, это может коснуться только одного пода, остальные продолжат работать, если у них нет проблем. Тогда для пользователя никакого простоя не будет.

Хм. Я, наверное, чего-то не понимаю. Вы подразумеваете, что кубер автоматически добавит памяти в контейнер, который упал из-за OOM?

_>Я уж не говорю о том, что ваш graceful shutdown при OOM это никак не решает. Памяти всё равно не хватило. О каких 24/7 вы говорили вообще?

Я повторно намекаю, что "всё равно не хватило" — это не единственный возможный сценарий ООМ. Если у нас там память течёт, то перезапуск контейнера с увеличенной памятью просто сделает чуть дольше ожидание следующего OOM.

_>И долго вы будете гадать на кофейной OOM-гуще, сколько памяти вам надо? А если другой поток/процесс потребует столько же памяти? Если вы хотите буфер 10G, может, вам изначально стоило озаботиться наличием необходимой памяти?

Ну вот я и пытаюсь ей изначально озаботиться — пробую выделить 10G. И меня очень разочарует паника и перезапуск в случае, если 10G нет. Хотелось бы чего-то более конструктивного.

_>Нет, в любой нормальной системе типов Optional Optional -- это Optional Optional, также как List of List of T -- это List of List of T, а не List of T. А если они у вас автоматически схлопываются, у вас дерьмовая система типов.

Tastes differ.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[6]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 12.01.25 11:35
Оценка:
Здравствуйте, Sinclair, Вы писали:


TB>>И вот тут оказывается, что у нас на дне дерева вызовов возвращается Result с каким-то Error для которого нет места в сигнатурах всех прочих функций.

S>Почему это?

Напоминаю твой пост:

S>И вот тут оказывается, что если у нас на дне дерева вызовов возникает специфическая ситуация, которую мы хотим обработать поближе к корню, то у нас связаны руки.


Ну и почему это такое случается? И почему если такое может случиться с Checked Exceptions то не может случиться с Result? Какая нафиг разница-то?

S>Автовывод решает эту проблему. Сигнатуры в явном виде пишутся там, где нам принципиален тип. И да, там нужно обрабатывать ошибку.


Как автовывод поможет тебе поправить сигнатуры всех промежуточных функций? Надеешься что они шаблонные?

TB>>Заимплементить SomeTrait если в сигнатурах есть Box<dyn SomeTrait>

S>Зачем?

Чтобы вернуть новый еррор для которого нет места в иерархии промежуточных функций.

S>>>3. Завернуть MySpecificException в UniversalException

TB>>Завернуть MySpecificError в UniversalError или скастить в UniversalError.
S>Зачем?

Чтобы вернуть новый еррор для которого нет места в иерархии промежуточных функций.

S>>>4. Выбросить потомка RuntimeException

TB>>Выбросить панику
S>Паника — это да, это наше всё.

А нафига тогда вводили все эти Result?

TB>>А нельзя тип исключения принять в качестве параметра шаблона?

S>А толку? Если у меня код бросает два разных типа исключения, то какой из них будет подставлен в качестве параметра шаблона? Most specific supertype?

Оба. Для вывода бросаемого лямбдой исключения чтоб суммировал типы исключений, бросаемые функциями, вызываемыми лямбдой.
Кстати, а что делает Раст если лямбда вызывает функции, которые возвращают Result с разными Error? Правильно, шлёт нафиг и требует смаппить к одному варианту.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[18]: Result objects - все-таки победили Exceptions?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 12.01.25 11:55
Оценка: 2 (1)
Здравствуйте, Sinclair, Вы писали:

_>>Это в теории, на практике -- нет. Даже в С/C++ и прочих языках с "ручным" контролем памяти что-то сделать сложно. Поэтому приходит OOM-Killer

S>OOM-Killer — это следствие криворукой архитектуры линукса. Программы под винду пишутся на тех же С/С++, но при этом там нет (и не нужно) никакого OOM-Killer.

1. Это не так. Там точно так же может быть переполнение по copy-on-write или по аллокации, вызванной неконтролируемыми затратами из ядра.
Это происходит реже, поэтому не все видели такую ситуацию. Но если происходит, то выйти из неё в разы сложнее — потому что нормальная защита в системе вообще не предусмотрена.
Лучше бы OOM killer был. Но ещё лучше, конечно, если бы в юниксах делали более умную обработку ситуации, а не просто убивать. Вариант с резервом я предлагал здесь.

2. Как минимум в линуксе предусмотрен костыль — overcommit policy может быть выставлено в режим "всегда полностью резервировать". При этом, да, потребуется в надцать раз больше свопа чем рамы, потому что слишком много обычных прикладух уже привыкли аллоцировать огромным куском наперёд.

S>Повторюсь: пример зависит от области деятельности и от того, где именно вылетел OOM. Если я получил OOM при создании шестибайтового объекта — да, скорее всего продолжать вообще смысла нет, и можно просто перезапускать весь контейнер или даже всю ноду. А если я пытаюсь выделить буфер размером в 100М, то шанс велик, что после вылета ООМ у меня всё ещё ~50М свободно. И можно заняться чем-то другим, не столь прожорливым к памяти. Или попробовать выделить буфер вдвое меньше.


Ну если тебя уже пристукнули по OOM, то продолжать сложно.

S> Ну, как пример — сортируем файл интов размером в 100гб. Понятно, что нужно делать сортировку слиянием; и понятно, что чем больше размер чанка, тем быстрее мы закончим.


Плохой пример, как ни удивит тебя. Во-первых, можно сделать просто mmap() файла в память и напустить какой-нибудь стандартный qsort, и он за счёт последовательных движений достаточно неплохо будет оптимизирован с точки зрения менеджера памяти. Во-вторых, массовый переход на timsort и тому подобные штуки показывает, что "чем больше размер чанка" уже давно не актуально. Это во времена написания TAOCP, возможно, было так.

Я бы взял в качестве примера что-то посерьёзнее, типа умножения матриц.

(про Optional уже обсудили)
The God is real, unless declared integer.
Re[14]: Result objects - все-таки победили Exceptions?
От: · Великобритания  
Дата: 12.01.25 16:43
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>>>Это не result object, а сообщение,

SD>·>А какая разница-то?
SD>Разница в том, что у вызывающего кода нет обязанности обработать его ровно там, где хочется обрабатывать только success/happy path. Код выглядит чистым и понятным, — код обработки ошибок вынесен туда, где достаточно контекста для оной обработки.
Я не очень понимаю. Как на ерланге написать код вроде
int add(string s1, string s2)
{
   int i1 = parse(s1);
   int i2 = parse(s2);
   return i1 + i2;
}

и как где там будет посылаться ошибка парсинга? И кем-когда обрабатываться?

SD>·>Угу.. Т.е. по сути On Error Resume Next™.

SD>Нет! В том-то и дело, что ровно наоборот: когда получен результат, продолжить выполнение. То есть в коде есть только happy path, а ошибки обрабатываются там, где им и положено.
Ну вот в случае parse — где им положено?

SD>Конкретно в случае с Erlang, его стандартная библиотека содержит т.н. "супервизоры", которые по дефолту представляют дерево retry'ев. Если ошибки никак не обрабатывать, а только указать failure domains, то сначала будут retry в самом внутреннем домене, потом в домене побольше, и так далее.

Про erlang я очень плохо знаю. Поэтому не очень понял суть этого высказывания.

SD>·>Ты куда-то не туда пошёл, уже к форматированию кода придираешься.

SD>Это все части одной истории, имя которой — читаемость кода. Если код замусорен всеми этими объявлениями checked exceptions, бессмысленной "обработкой ошибок" в стиле "запиши в лог и пробрось выше", да еще и отформатирован вот так — одна строчка превращается в 20. И прочитать пять вложенных вызовов занимает час вместо 5 минут.
Так сложно вести беседу. Получается: "А вот исключения такие-то", а ты "но вот в криво отформатированном коде всё плохо". Отформатируй код по-другому. Делов-то..

SD>·>Так java-like unchecked исключения — оно и есть.

SD>Так я за них и ратую, и оправдываю C#-стиль без checked вообще.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[19]: Result objects - все-таки победили Exceptions?
От: · Великобритания  
Дата: 12.01.25 16:47
Оценка: +1
Здравствуйте, korvin_, Вы писали:

_>Это проблематично сделать, учитывая, что даже String (любая конкатенация == аллокация) и любой массив в Java -- Object. При этом нужно быть осторожным с использованием любого библиотечного кода, который может что-то аллоцировать, в том числе и неявно, и даже языковых конструкций, типа for (var x : array) { ... }, т.к. она приводит к аллокации итератора.

Да полно low или даже zero gc кода. Это не бином ньютона, по крайней мере в мире Java.

_>Удачи "выделить сразу всё на старте".

Не rocket science.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.01.25 06:00
Оценка: +1
Здравствуйте, T4r4sB, Вы писали:

S>>И вот тут оказывается, что если у нас на дне дерева вызовов возникает специфическая ситуация, которую мы хотим обработать поближе к корню, то у нас связаны руки.

TB>Ну и почему это такое случается?
TB>И почему если такое может случиться с Checked Exceptions то не может случиться с Result? Какая нафиг разница-то?
Потому что checked exceptions прибиты там гвоздями. Нет в языке никакой возможности сказать "а если меня позвали с параметром, вызов которого бросает неизвестный X, то добавьте этот X к моей сигнатуре".
S>>Автовывод решает эту проблему. Сигнатуры в явном виде пишутся там, где нам принципиален тип. И да, там нужно обрабатывать ошибку.
TB>Как автовывод поможет тебе поправить сигнатуры всех промежуточных функций? Надеешься что они шаблонные?
Почему шаблонные? Просто у них не указан вообще никакой тип. В каком-нибудь flow9 это прекрасно работает. Несмотря на отсутствие шаблонов

TB>Чтобы вернуть новый еррор для которого нет места в иерархии промежуточных функций.

Конечно есть место.

TB>А нафига тогда вводили все эти Result?

Для того, чтобы обойтись без паники там, где мы хотим обойтись без паники.

TB>Оба. Для вывода бросаемого лямбдой исключения чтоб суммировал типы исключений, бросаемые функциями, вызываемыми лямбдой.

Да, это было бы значительно лучше. Возможно, в эту сторону и нужно копать — ваши с korvin_ примеры убеждают меня в том, что локально-понятные result object на большой сodebase приводят к примерно такому же бардаку, как и локально-понятные checked exceptions.
Впрочем, наверняка есть какой-то способ изящно объединить обе парадигмы во что-то одно.
TB>Кстати, а что делает Раст если лямбда вызывает функции, которые возвращают Result с разными Error? Правильно, шлёт нафиг и требует смаппить к одному варианту.
Ну, а вот в каком-нибудь TypeScript просто произойдет сложение типов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 07:37
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Почему шаблонные? Просто у них не указан вообще никакой тип. В каком-нибудь flow9 это прекрасно работает. Несмотря на отсутствие шаблонов


В Русте скорее всего в любой либе будут явно указаны все возможные ошибки.
Хотя в языке есть шаблоны. Просто шаблонные функции это зачастую лишняя нагрузка на компилятор.
flow9 — это что-то динамическое что ли? А если там автовывод то что если мы два оаза вызовем функцию передав туда разные лямбды возвращающие разные результаты?

TB>>Чтобы вернуть новый еррор для которого нет места в иерархии промежуточных функций.

S>Конечно есть место.

Почему с чекед ексепшонами его нет а с резултами есть?

TB>>А нафига тогда вводили все эти Result?

S>Для того, чтобы обойтись без паники там, где мы хотим обойтись без паники.

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

TB>flow9 — это что-то динамическое что ли? А если там автовывод то что если мы два оаза вызовем функцию передав туда разные лямбды возвращающие разные результаты?

Нет, полная статика. Но я вас обманул — шаблоны там всё-таки есть, хотя в большинстве случаев они автовыводятся.

TB>Почему с чекед ексепшонами его нет а с резултами есть?

Потому что автовывода ексепшн сигнатуры я не видел, а автовывод типа результата — видел.
В том же тайпскрипте вот такая функция:
function foo(b: boolean)
{
  return b ? bar(): baz()
}

function bar(): number | Error1;
function baz(): number | Error2;

имеет тип number | Error1 | Error2.

TB>А если чисто логически по смыслу паника не нужна но мы не можем протащить новый еррор через функции левой библиотеки?

Да почему ж не можем-то?

Вот у нас "левая библиотека":
function foo<T1, T2>(b: boolean, a: ()=>T1, c: ()=>T2)
{
  return b ? a(): c()
}

Если мы в неё скормим bar и baz, то результат по-прежнему будет иметь тип number | Error1 | Error2.
А если bar и bar, то number | Error1.

И мы можем это отловить через ts-pattern.

См. тж.
https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAbziAhjAxgCwDRwApwC+cAZlBCHAOQwDOAtGGjAKZQB2VA3AFA-DtWUEinQs4AUSjkoARkQgWtWigDmLAFxxaMKANWFeAoSLGTp0AEyI2MgMIQAJprjsAriABGbQ3xJv2dBhgCHZSCAgACk8tTwiAGxYUdgBKHgQeODgoFhg3DjhPOAB+QpQoSJTYlAAvSp5CPwCgkLDPcsqtdy82OAAfcxlZdMys7Nz8sIAWS15G-0Dg0LK6qtcPbyh+wasRsfG8gtkAdjmmxda4FgAPIXYUeIAxCIAeABVZXDfLAD5o2ISSXYuBQWkqAF4fh9cOgwSlId80hksjlDm0SnAUJ04Oh6o0eAsWstUAJ-oVAckkaMsol4Nc4OCrrc2PcnhForh2lBObUUrx9gB6AW0uAATwZcHpAGo4LIuHAhXAAPJgNhoaDUKVUHHJdgQeDeTFgMDxYAsRxwGAQS2i1W0ajdTbbKRDZ0WKCWbXJC1UR1sKgAOkilgAzAA2ACsaX2IvFjNQGEwkWu0f2cADAHdgDAk3gA37uXBk-CftcpbJU-tM9mkwhFMo1C48zo9OwDLhInWlCp1IQUgyfnB6McAJyWStjAM3TAoNw6YAANxYlV4DSAA
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 09:24
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>Потому что автовывода ексепшн сигнатуры я не виде


Так это вопрос не к идее проверяемых исключений а к реализации

> а автовывод типа результата — видел.


А я в Расте не видел автовывод типа ошибки за пределами библиотек совсем общих алгоритмов

TB>>А если чисто логически по смыслу паника не нужна но мы не можем протащить новый еррор через функции левой библиотеки?

S>Да почему ж не можем-то?

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

TB>Потому что такая библиотека и все ошибки явно прописали в сигнатуре

Значит, это плохая библиотека. Проблема с единственной в природе реализацией checked exceptions — в том, что в ней невозможно написать хорошую библиотеку*.
А языки с автовыводом типов не просто позволяют, а прямо-таки провоцируют писать хорошие библиотеки.
Внезапно оказывается, что многие библиотеки для тайпскрипта как раз работают с обобщёнными типами, и это вовсе не исчерпывается "функциональщиной", ограниченной map/reduce/filter.
Вполне себе гражданские вещи вроде тех же VSCode-библиотек или парсеров отлично работают примерно с чем угодно, и проблемы "я не смог вернуть нужный мне тип результата из лямбды" у прикладного программиста возникают крайне редко.

Это даёт мне повод надеяться на то, что данное направление стоит считать конструктивным.

* есть шанс на то, что современная Java всё-таки позволяет улучшить ситуацию; я на ней уже давно не пишу, поэтому уверенности нету. Но можно попробовать показать мне способ, который бы давал возможность написать хотя бы функцию map так, чтобы ей можно было пользоваться для checked-парсинга стрима строк в стрим интов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[12]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 11:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


TB>>Потому что такая библиотека и все ошибки явно прописали в сигнатуре

S>Значит, это плохая библиотека. Проблема с единственной в природе реализацией checked exceptions — в том, что в ней невозможно написать хорошую библиотеку*.

Я смотрю на Раст и там может и можно написать библиотеку выводящую типа ошиьки, но по факту никто так не делает в прикладных либах.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[13]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.01.25 12:28
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>Я смотрю на Раст и там может и можно написать библиотеку выводящую типа ошиьки, но по факту никто так не делает в прикладных либах.
Может быть, потому что нельзя? Или очень сложно / контр-идиоматично?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 12:49
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>Может быть, потому что нельзя? Или очень сложно / контр-идиоматично?


Можно, так делают в алгоритмах. А в функции парсинга джсона в структуру — уже свой Еррор один на всю либу. А в функции открытия файла — свой еррор. А если ты хочешь передать что твоя функция может обломаться тзза отсутствия файла или кривого содержимого файла — то проще всего промапить в Box<dyn Error>, но никакого автовывода множества возможных ошибок не будет. Зато хотя бы ? поддерживает автокаст любой ошибки в trait Error
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[15]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.01.25 13:32
Оценка:
Здравствуйте, T4r4sB, Вы писали:
TB>Можно, так делают в алгоритмах. А в функции парсинга джсона в структуру — уже свой Еррор один на всю либу. А в функции открытия файла — свой еррор. А если ты хочешь передать что твоя функция может обломаться тзза отсутствия файла или кривого содержимого файла — то проще всего промапить в Box<dyn Error>, но никакого автовывода множества возможных ошибок не будет. Зато хотя бы ? поддерживает автокаст любой ошибки в trait Error
Ну вот я и говорю — то, что в тайпскрипте работает само по себе из коробки, в расте требует каких-то унизительных приседаний, если вообще работает.
Я же показал код функции, которая смешивает типы ошибок. Там нет никаких ручных маппингов.
И её необязательно писать именно в виде single expression.
Можно писать в императивном стиле, с множественными return — и всё равно выведется общий тип.
Конкретно в тайпскрипте неудобно делать постоянные проверки, но он и не ставил перед собой цель порождать provably correct programs:
function foo()
{
   let x = bar();
   if(!isNumber(x)) return x;
   let y = baz();
   if(!isNumber(y)) return y;
   return x+y;
}

Не знаю, насколько ужасно аналог такого кода будет выглядеть на Расте.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Result objects - все-таки победили Exceptions?
От: ononim  
Дата: 13.01.25 13:38
Оценка: -2
S>Но, как оказалось, народ идеи не понял. Так и вернулись к понятным дебилоидам кодам возврата, т.к. проверяемые исключения осилить не смогли. Так же и в Kotlin их решили не делать.
Я думаю концепция GetLastError()/errno недооценена
Но ее надо чутка расширить, параметром facility.И API сделать таким:
void SetError(FacilityID facility, ErrorObject error);
ErrorObject *GetError(FacilityID facility, bool reset = false);


Поясню, с классическими кодами ошибок проблем две
— это всего лишь код
— бойлерплейтный код для их проверок везде подряд

С исключениями эти проблемы решены, но есть другие две
— редкий бойлерплейтный код чтобы их ловить, а если забудут словить — все падает, от чего у джуниоров пригорает и они бегут в раст
— на многих системах они пц тормозные (если используются), но ради справедливости — они почти что zero-overhead если не используются.

Подобные аспектно-ориентированные last-error коды ошибок не имеют ни одной из этих проблем. Я у себя юзал такой подход, очень удобно. Типа пишешь в файл длинную сериализацию чего либо, и в конце просто проверяешь — все ли записи в файл прошли гладко. Удобно.
Для любителей ООП, возможна вариация — вместо глобальной SetError/GetError сделать их методами интерфейс ErrorHolder, который реализуется всеми объектами, которые могут совершить ошибку. И тогда код с сериализацией в файл будет выглядеть так:
FileObject fo("/foo/bar", "w");
fo.WriteString("foo");
fo.WriteString("bar");
fo.WriteInteger(123);
fo.Flush();
if (fo.GetError()) ShowMessage(fo.GetError(true)->Text);
Как много веселых ребят, и все делают велосипед...
Отредактировано 13.01.2025 13:44 ononim . Предыдущая версия . Еще …
Отредактировано 13.01.2025 13:43 ononim . Предыдущая версия .
Отредактировано 13.01.2025 13:43 ononim . Предыдущая версия .
Отредактировано 13.01.2025 13:39 ononim . Предыдущая версия .
Отредактировано 13.01.2025 13:39 ononim . Предыдущая версия .
Отредактировано 13.01.2025 13:38 ononim . Предыдущая версия .
Re[16]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 13.01.25 18:52
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>
S>function foo()
S>{
S>   let x = bar();
S>   if(!isNumber(x)) return x;
S>   let y = baz();
S>   if(!isNumber(y)) return y;
S>   return x+y;
S>}
S>

S>Не знаю, насколько ужасно аналог такого кода будет выглядеть на Расте.

А что такое x, y для начала? Тогда я смогу описать этот код на Расте.
В самом простом случае код такой:
fn bar() -> Result<i32, BarError> { ... }
fn baz() -> Result<i32, BazError> { ... }

fn foo() -> Result<i32, Box<dyn Error>> { 
  Ok(bar()? + foo()?)
}
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.