Re[6]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 17:20
Оценка:
S>Что с этим делать — решает пользователь этого парсера. Он может прямо тут же заматчить ошибку синтаксиса и, скажем, подставить дефолтный конфиг; или сделать retry, или просто прокинуть результат выше по стеку, делегировав принятие решения туда.

Последний вариант (прокинуть дальше) наиболее частый и вероятный в реальном коде. Поэтому он и должен работать по умолчанию (безо всяких ?)

S>Компилятор будет бить по рукам за попытку прочитать значение параметра из экземпляра типа "myConfig | BadSyntax".


Отвратительно. Теперь вместо processData(getData()) придется писать простыню из десятка строчек кода с имитацией исключений путем проброса BadSyntax ответа выше.

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


Так это как раз идеальный вариант! Код получается чистым, читабельным, легко модифицируемым. Исключения начинают существовать только на том уровне, где они МОГУТ быть обработаны. Вместо того, чтобы на каждом уровне из 100 писать "на этом уровне обработать не могу, бросай дальше".

S>Любой словарик из метода get(key: K) возвращает не V, а Maybe<V>, и нужно обязательно проверить, нашлось ли значение по ключу. А для типового случая есть перегрузка get(key: K, default: V), которая позволяет обойтись без паттерн-матчинга результата. Ну, или язык умеет в x: V = mydict.get(k) ?? defX; вместо x: V = mydict.get(k, defX);, и тогда даже и перегрузка не нужна.


Так это все тот же подход Elixir'а с !. Пример — https://hexdocs.pm/elixir/1.18.1/Map.html#fetch/2 (см. вариант с ! и без !).

Сдается мне, без этого никак.
Re[8]: Result objects - все-таки победили Exceptions?
От: · Великобритания  
Дата: 09.01.25 17:22
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

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

S>·>Тогда это становится по сути аналогом checked exceptions. В каждом методе придётся пробрасывать всю это портянку T | OutOfMemory | StackOverflow | ByteCodeVerificationError | DivisionByZero | NullPointer | ....
S>Во-первых, "пробрасывать" в таком случае гораздо легче, чем в случае checked exceptions. Потому что мне не нужно выписывать всю сигнатуру.
S>Достаточно обработать happy path а остальное вернуть как есть:
S>
S>let x = getX(); // number | OutOfMemory | StackOverflow | ByteCodeVerificationError | DivisionByZero | NullPointer | ....
S>return (x.isNumber() 
S>  ? (x * 3.14).toString()
S>  : x) // string | OutOfMemory | StackOverflow | ByteCodeVerificationError | DivisionByZero | NullPointer | .... 
S>


S>·>Если у меня есть большая портянка кода, которая считает скидку — ну если что-то где-то там навернулось, я это могу поймать на верхнем уровне и не применить скидку, чем просто терять весь заказ или вообще завалить весь процесс с panic.

S>Совершенно верно.
S>
S>let discount = getDiscount(); // number | OutOfMemory | StackOverflow | ByteCodeVerificationError | DivisionByZero | NullPointer | ....
S>discount = isNumber(discount) ? discount : 0; // number
S>totalAmount *= (1 - discount);
S>

Тип discount будет вырвиглазный. Для ЯП с статической типизацией — фу.
А если положить этот discount в поле класса?
Ещё, само нутро getDiscount() будет кишеть этими isNumber и ? обработкой ошибок. Можно писать так:
double discount(Order o)
{
  double s = o.getStuff();// | OutOfMemory | StackOverflow | ByteCodeVerificationError | DivisionByZero | NullPointer | ....
  double x = db.loadX(o);// | + SQL Exceptions
  double y = rest.goSomewhere(x, s);//| + Rest Exceptions
  return s + x / y; // | ArithmeticOverflow | DivisionByZero
}

а ты как предлагаешь?

S>Точно ли вы хотите писать вместо этого

S>} catch(Exception e) {}

S>?
А что в этом плохого?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 17:38
Оценка:
·>Это просто одна из моделей конкурентности — акторы. Можно Скалу посмотреть, там такое есть на уровне библиотек, не нужно вкорячивать в синтаксис ЯП.

Scala из-за этого сложнее читать. Да и в целом забавная судьба у Akka, и у некоторых аналогов.

·>
SD>>    {success, Result} -> Result;
·>

·>Т.е. никаких исключений, а result objects. ЧТД.

Это не result object, а сообщение, и tagged record я просто как пример привел, для демонстрации работы pattern matching. Чтобы было видно, что там можно заматчить то, что тебе нужно на этом уровне обрабатывать.

·>И теперь проблема разобраться — откуда этот 'EXIT' пришел/может прийти и как оно всё навернётся если вместо этого 'QUIT' какой-нибудь придёт.


'EXIT' приходит с пояснениями (Reason). Если придет 'QUIT', он так и останется лежать в очереди, пока кто-то его оттуда не достанет и не попробует обработать.

SD>>Очень выразительно. Мне недавно пришлось примерно такое на C# писать, так там 4 экрана кода и всякая магия c DI.

·>Что-то странно.

Никакой странности, C# все-таки уж очень Java-inspired. Плюс все эти фреймворки, да best practices, да идиотский code formatter, требующий писать так:

if (success) 
{
  run_useful_code();
}
else
{
  run_another_code();
}


·>Это улучшает ситуацию, но не совсем решает...


Думаю, в моем предыдущем ответе Sinclair стало понятно, что у нас разные взгляды на то, как должен выглядеть код.

Я хочу, чтобы код действительно читался как "исключений не бывает" — только happy path. А обработка ошибок явно происходит только на тех уровнях, где это действительно можно сделать, а не "обработать" в стиле "запишем в лог и пробросим дальше".
Re[7]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 17:43
Оценка:
_>Поэтому, закончилось всё макросами, которые уже на автомате пишут, чтобы компилятор по рукам не бил а позволял сразу использовать результат не вникая в ошибку. Пишем тупо в конце каждой функции '?', и всё. Сведение этого бойлерплейта к пустой строке ("") — как раз и есть превращение Result Object в самый обычный Exception.

Во!
Я ровно это и написал уже несколько раз другим участникам. Поскольку это наиболее частый случай (пробросить дальше), как раз и нужно чтоб он не требовал никакого кода и прочих значков типа ?. И значок ! тоже не нужен, т.к. он означает "компилятор, на этом уровне я хочу обработать ошибку". Но в таком варианте нужно писать не !, а код обработки
писать код обработки.

_>С кодами ошибок (или с result object) — проблема та же самая. Вот типовой оптимистичный код (прямо из руководства по Rust):




_>Для кодеров, которые не пишут обработчики, а пробрасывают всё наверх, разница с исключениями только в том, что в случае исключений match, try! или '?' писать не нужно. Это почти такое же зло как ON ERROR RESUME NEXT в VB. Только теперь это ON ERROR RETURN FALSE. У нас как-бы заявлена, как-бы явная обработка ошибок, только она ...неявная


Йес!
Re[8]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 17:49
Оценка: 1 (1) +1
S>Точно ли вы хотите писать вместо этого
S>
S>try
S>{
S>  totalAmount *= (1 - getDiscount());
S>} catch(Exception e) {}
S>

S>?

В реальности этот код будет выглядеть так:

try {

<...100500 stack frames...>

  totalAmount *= (1 - getDiscount());

<...100500 more frames...>

} catch (BadFileException)
   ...
  catch (TooPoorException)
   ...
  catch (NumberConvertException)
   ...
  catch (Exception)
   <тут какой-то совсем generic handler типа "не шмогла")


И не мой личный вкус это наиболее чистый вариант. Код обработки расположен там и только там, где оную обработку сделать можно. Нигде в других местах никаких ? нет.
Re[12]: Result objects - все-таки победили Exceptions?
От: · Великобритания  
Дата: 09.01.25 17:57
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>·>Это просто одна из моделей конкурентности — акторы. Можно Скалу посмотреть, там такое есть на уровне библиотек, не нужно вкорячивать в синтаксис ЯП.

SD>Scala из-за этого сложнее читать. Да и в целом забавная судьба у Akka, и у некоторых аналогов.
Ну это выходит за рамки обсуждения сабжа.

SD>·>[/code]

SD>·>Т.е. никаких исключений, а result objects. ЧТД.
SD>Это не result object, а сообщение,
А какая разница-то?

SD> и tagged record я просто как пример привел, для демонстрации работы pattern matching. Чтобы было видно, что там можно заматчить то, что тебе нужно на этом уровне обрабатывать.

С т.з. дизайна ЯП — неважно что паттерн-матчить. Если erlang умеет матчить только сообщения, но не умеет ud-типы — ну такое себе...

SD>·>И теперь проблема разобраться — откуда этот 'EXIT' пришел/может прийти и как оно всё навернётся если вместо этого 'QUIT' какой-нибудь придёт.

SD>'EXIT' приходит с пояснениями (Reason). Если придет 'QUIT', он так и останется лежать в очереди, пока кто-то его оттуда не достанет и не попробует обработать.
Угу.. Т.е. по сути On Error Resume Next™.

SD>>>Очень выразительно. Мне недавно пришлось примерно такое на C# писать, так там 4 экрана кода и всякая магия c DI.

SD>·>Что-то странно.
SD>Никакой странности, C# все-таки уж очень Java-inspired. Плюс все эти фреймворки, да best practices, да идиотский code formatter, требующий писать так:
Ты куда-то не туда пошёл, уже к форматированию кода придираешься.

SD>·>Это улучшает ситуацию, но не совсем решает...

SD>Думаю, в моем предыдущем ответе Sinclair стало понятно, что у нас разные взгляды на то, как должен выглядеть код.
SD>Я хочу, чтобы код действительно читался как "исключений не бывает" — только happy path. А обработка ошибок явно происходит только на тех уровнях, где это действительно можно сделать, а не "обработать" в стиле "запишем в лог и пробросим дальше".
Так java-like unchecked исключения — оно и есть.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Result objects - все-таки победили Exceptions?
От: · Великобритания  
Дата: 09.01.25 18:01
Оценка:
Здравствуйте, SkyDance, Вы писали:

S>>Точно ли вы хотите писать вместо этого

SD>В реальности этот код будет выглядеть так:
Почему? Разные catch нужны если только надо делать разные действия. Обычно хватает catch (Exception), достаточно зарепортить исключение, пропустить применение скидки и может куда-то инфу послать, что нешмогла.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 18:07
Оценка:
SD>>Это не result object, а сообщение,
·>А какая разница-то?

Разница в том, что у вызывающего кода нет обязанности обработать его ровно там, где хочется обрабатывать только success/happy path. Код выглядит чистым и понятным, — код обработки ошибок вынесен туда, где достаточно контекста для оной обработки.

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


Нет! В том-то и дело, что ровно наоборот: когда получен результат, продолжить выполнение. То есть в коде есть только happy path, а ошибки обрабатываются там, где им и положено. Конкретно в случае с Erlang, его стандартная библиотека содержит т.н. "супервизоры", которые по дефолту представляют дерево retry'ев. Если ошибки никак не обрабатывать, а только указать failure domains, то сначала будут retry в самом внутреннем домене, потом в домене побольше, и так далее.

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


Это все части одной истории, имя которой — читаемость кода. Если код замусорен всеми этими объявлениями checked exceptions, бессмысленной "обработкой ошибок" в стиле "запиши в лог и пробрось выше", да еще и отформатирован вот так — одна строчка превращается в 20. И прочитать пять вложенных вызовов занимает час вместо 5 минут.

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



Так я за них и ратую, и оправдываю C#-стиль без checked вообще.
Re[10]: Result objects - все-таки победили Exceptions?
От: SkyDance Земля  
Дата: 09.01.25 18:09
Оценка:
·>Почему? Разные catch нужны если только надо делать разные действия.

Я именно это и проиллюстрировал, если нужно делать разные действия — делай их там, где есть весь контекст для совершения оных действий.
Re[4]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 09.01.25 18:24
Оценка: +1
Здравствуйте, Sinclair, Вы пи

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


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

S>Середина этого дерева знать не знает про эту специфическую ситуацию; её спроектировали в те времена, когда никому и в голову не приходило, что там внутри может что-то сломаться таким способом.

S>В мире checked exceptions у нас есть три плохих способа это решить:

В мире Result у нас есть три плохих способа это решить:

S>1. Пройтись вверх по стеку вызовов и везде подправить сигнатуры, добавив MySpecificException к списку


Пройтись вверх по стеку вызовов и везде подправить сигнатуры, добавив MySpecificError к списку возможных Error (это конечно если Error вообще предусматривает различные ошибки)

S>2. Отнаследоваться от какого-то исключения, которое в сигнатурах уже есть

S>Это если в сигнатурах есть хоть что-то, и это что-то — не final.

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

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

S>Это если в сигнатурах есть UniversalException, в котором предусмотрено место для InnerException

Завернуть MySpecificError в UniversalError или скастить в UniversalError.

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


Выбросить панику

S>Написать функцию IEnumerable<R> map(IEnumerable<T> source, Func<T, R> f) c проверяемыми исключениями не представляется возможным. В ней функция f не имеет права ничего выбрасывать, потому что map пообещала ничего не выбрасывать.


А нельзя тип исключения принять в качестве параметра шаблона?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[6]: Result objects - все-таки победили Exceptions?
От: T4r4sB Россия  
Дата: 09.01.25 18:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>А теперь мы остаёмся наедине непонятно с чем:


Ну в одном случае перечислили FileAccessException, в другом нет. А что мешает во втором его перечислить / не забыть перечислить?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[8]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 09.01.25 19:56
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


_>>Что делать в языках с GC без явного разграничения стэк/куча? Возьмём, к примеру, Hashell или Ocaml. Куда добавить OutOfMemory?

S>Простите, я не понимаю ваш вопрос. Мне, если честно, строго всё равно, откуда malloc берёт память — со стека или из кучи.
S>Может, она вообще для одних n берёт из одного места, а для других — из другого.
S>Но как только malloc может закончиться неудачей, я добавляю это в сигнатуру malloc.
S>Опять же — если я считаю, что пользователю достаточно признака успешности, то у меня получается (T[] | undefined), он же Optional<T[]>.
S>А если мне интересны сценарии вроде "если не хватило памяти для X, давайте захватим X/2", то может потребоваться более интересная сигнатура, вроде (T[] | OutOfMemory), где OutOfMemory — это не унитарный тип, а некая структура с подробностями вроде хинтов на предмет того, какие n были бы приемлемыми.

Хорошо, давайте на примере.

Сначала Java:

class Foo {
    ...
    // constructor
    Foo(...) {
        ...
    }
    ...
}

...

var x = new Foo(...);


Где здесь должно быть указано, что new Foo может вернуть не объект класса Foo, а OutOfMemory? В сигнатуре конструктора Foo? В "сигнатуре" оператора new? Тот же вопрос про StackOverflow: где он должен быть указан? В сигнатуре каждой функции (метода)? Операция () вызов метода автоматически должна его добавлять к каждому методу?

Теперь Haskell. Имеем такой код, например:

data Expr
  = Val Int
  | Add Expr Expr
  | Mul Expr Expr
  | If Expr Expr Expr

eval :: Expr -> Expr
eval e = ...

expr0 = If (Mul (Val 1) (Val 0))
           (Add (Mul (Val 4) (Val 10))
                (Val 2))
           (Mul (Add (Val 7) (Val 14))
                (Val 2))

print (eval expr0)) -- Val 42


Где тут должено быть указано OutOfMemory?
Re[5]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.01.25 03:51
Оценка:
Здравствуйте, T4r4sB, Вы писали:

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

Почему это?
S>>Середина этого дерева знать не знает про эту специфическую ситуацию; её спроектировали в те времена, когда никому и в голову не приходило, что там внутри может что-то сломаться таким способом.
TB>В мире Result у нас есть три плохих способа это решить:

S>>1. Пройтись вверх по стеку вызовов и везде подправить сигнатуры, добавив MySpecificException к списку

TB>Пройтись вверх по стеку вызовов и везде подправить сигнатуры, добавив MySpecificError к списку возможных Error (это конечно если Error вообще предусматривает различные ошибки)
Автовывод решает эту проблему. Сигнатуры в явном виде пишутся там, где нам принципиален тип. И да, там нужно обрабатывать ошибку.

S>>2. Отнаследоваться от какого-то исключения, которое в сигнатурах уже есть

TB>Заимплементить SomeTrait если в сигнатурах есть Box<dyn SomeTrait>
Зачем?
S>>3. Завернуть MySpecificException в UniversalException
TB>Завернуть MySpecificError в UniversalError или скастить в UniversalError.
Зачем?
S>>4. Выбросить потомка RuntimeException
TB>Выбросить панику
Паника — это да, это наше всё.
S>>Написать функцию IEnumerable<R> map(IEnumerable<T> source, Func<T, R> f) c проверяемыми исключениями не представляется возможным. В ней функция f не имеет права ничего выбрасывать, потому что map пообещала ничего не выбрасывать.

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

А толку? Если у меня код бросает два разных типа исключения, то какой из них будет подставлен в качестве параметра шаблона? Most specific supertype? Ну, тогда просто в 99% будет подставляться Exception, и ещё в 1% — Throwable. Что, собственно, эквивалентно отключению checked exceptions вообще.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.01.25 03:53
Оценка:
Здравствуйте, T4r4sB, Вы писали:

TB>Ну в одном случае перечислили FileAccessException, в другом нет. А что мешает во втором его перечислить / не забыть перечислить?

Отсутствие поддержки компилятором. То есть мы получили недостатки checked exceptions без преимуществ checked exceptions.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.01.25 04:05
Оценка:
Здравствуйте, korvin_, Вы писали:

_>Где здесь должно быть указано, что new Foo может вернуть не объект класса Foo, а OutOfMemory? В сигнатуре конструктора Foo? В "сигнатуре" оператора new?

Примерно там же, где сейчас указано, что new Foo может выбросить OutOfMemoryError. То есть — в "сигнатуре" оператора new.

_>Тот же вопрос про StackOverflow: где он должен быть указан? В сигнатуре каждой функции (метода)? Операция () вызов метода автоматически должна его добавлять к каждому методу?

Если идти по строгому пути — то да, в сигнатуре каждой функции.

_>Теперь Haskell. Имеем такой код, например:


_>
_>data Expr
_>  = Val Int
_>  | Add Expr Expr
_>  | Mul Expr Expr
_>  | If Expr Expr Expr

_>eval :: Expr -> Expr
_>eval e = ...

_>expr0 = If (Mul (Val 1) (Val 0))
_>           (Add (Mul (Val 4) (Val 10))
_>                (Val 2))
_>           (Mul (Add (Val 7) (Val 14))
_>                (Val 2))

_>print (eval expr0)) -- Val 42
_>


_>Где тут должено быть указано OutOfMemory?

Хороший вопрос. Надо полагать — везде.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Result objects - все-таки победили Exceptions?
От: Pavel Dvorkin Россия  
Дата: 10.01.25 05:47
Оценка:
Здравствуйте, Sinclair, Вы писали:

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


PD>>Внизу у нас (в рамках примера с cloud) код, который работает с этой cloud file system, и знать про HTTP ничего не должен.

S>Конечно же должен. Потому что клаудная "FS" — это некий Web API. Который, естественно, отвечает нам HTTP-шными кодами. Например — 403 с подробностями "token expired", которые совершенно непонятны среднему слою, зато понятны верхнему — который знает, что нужно токен обновить и попробовать снова.

Так, давай по порядку.

До того, как мы добавили Cloud FS, все выглядело так

1. Верхний уровень. Получает от среднего (треш и угар) HTTPException, решает что в этом случае делать.
2. Средний уровень. Работает с HTTP, черт знает как, но работает. Иногда выбрасывает HttpException. Обращается к нижнему уровню для получения каких-то файлов.
3. Нижний уровень. Работает с ФС. Пока что у нас там или NTFS, или какая-то линуксовская ФС.

Откуда тут идут HTTPException ? Очевидно, только от среднего слоя. Не может нижний слой их генерировать. Никак не может.

Все так ?

Теперь вместо NTFS у нас некая CloudFS.

Ну если ты ФС, то будь добра вести себя как ФС. Собственно, даже не будь добра, а никуда не денешься. Потому что средний слой обращается к тебе как к ФС и не ждет от тебя никакого HTTP, равно как и FTP и т.д. Он работает по АПИ ФС, и только ошибки этого АПИ он может обрабатывать. Так что будь добра все свои HTTP/FTP проблемы решать сама, а в средний слой возвращать что-то связанное с ошибками файлов и потоков и тому подобным. IOException, например

Но не получилось. Иногда этот нижний слой выбрасывает XyzException, связанный с HTTP. Средний слой его не ждет и вообще-то обрабатывать не может. Поэтому они идет выше, в верхний слой, а это значит, что средний слой вообще-то потерпел неудачу — он вызвал нижний, а там необрабатываемая проблема. Значит, средний слой свое дело вообще не выполнил. Так что все верно, она попадает в верхний как неожиданное исключение. Просил средний слой какой-то файл ему дать, а в ответ — XyzException.

Короче, верхний слой должен ловить HttpException от среднего, именно там и идет работа с HTTP. А не от нижнего.


S>>>Нет. Никто там от С++ не наследовался. И подозревать причины нет — все ходы записаны, включая ход мысли отцов-основателей.

PD>>Допускаю. Равно как допускаю, что они неосознанно все же были под влиянием MSVC++. Не могли они его не знать.
S>Это очень странный ход рассуждений. Во-первых, с тем же успехом можно сказать, что они были под влиянием Pascal — ведь там тоже нет checked exceptions.

Ну в Pascal как он был вначале их нет вообще. Вроде как и в TurboPascal их не было, не помню точно. Вот в Delphi точно были.

>То есть наличие фичи откуда-то унаследовать можно; а вот унаследовать отсутствие фичи....


Вполне можно. Там ее не было, и тут не будет. Унаследовали отсутствие А то, что она есть в параллельном проекте (Java) — нам не указ.

S>Во-вторых, в С++ исключения всё же входят в сигнатуру, хоть и не проверяются. В дотнете — нет, не входят. То есть "наследование" какое-то очень однобокое.


PD>>Тут будет, да. Но это особая ситуация. Произошло что-то непредвиденное.

S>Нет тут ничего особенного. Исключения и придуманы для "непредвиденных" ситуаций. Предвиденные неприятности принято обрабатывать явно.

checked exceptions и есть предвиденные неприятности, и их обрабатывают явно.

S>Вовсе не везде. В целом, конечно же, крайне полезно знать, что в некоторых местах NullPointerException возникнуть не может.


Если NPE может произойти, то оно произойдет. Если NPE произойти не может, оно все равно произойдет.

(C) моя перефразировка закона Мерфи.

S>Это всё работает только в плоских иерархиях вызовов. И совершенно никак не работает в коде на колбеках. Который, собственно, и является наиболее частоиспользуемым примером, когда мы контролируем "дно" и "крышу", а середину хотим куда-то делегировать.


Основная суть проблемы вот в чем. Ты доказываешь, что применение checked не панацея и приводишь примеры, когда они не очень успешно могут быть применимы. Я не спорю, не всегда все хорошо получится. Но зачем же, если нельзя обеспечить 100% успеха, заявлять, что это вообще никуда не годится ? Если во многих случаях это обеспечивает, что мы имеем меньше шансов забыть что-то важное, то почему этим не воспользоваться ? Пусть даже в других случаях это и не работает.

Вот тебе простой пример.

void read(char * filename){
FILE* f = fopen(filename, "rb");
// чтение
fclose(f);
}

написал и запустил . Работает как часы, пока файл есть. А если его вдруг не окажется, будет хоть и не исключение (это же С), но плохо будет. А я забыл проверку поставить.


void read(String filename) {
FileInputStream fis = new FileInputStream(new File(filename);
// чтение
close(fis);
}

Не компилируется. На FileInputStream диагностика unhandled exception FileNotFoundException. На close — IOException.
Иди-ка, автор, и подумай, что ты делаешь. А если файла нет ? А если закрытие не прошло почему-то ? Изволь подумать, что делать и код написать.

Ну и что тут плохого ? Бог с ними, с верхними и средними уровнями и колбеками, но тут-то что плохого ?
With best regards
Pavel Dvorkin
Re[10]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 10.01.25 06:25
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

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


_>>Где здесь должно быть указано, что new Foo может вернуть не объект класса Foo, а OutOfMemory? В сигнатуре конструктора Foo? В "сигнатуре" оператора new?

S>Примерно там же, где сейчас указано, что new Foo может выбросить OutOfMemoryError. То есть — в "сигнатуре" оператора new.

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

_>>Тот же вопрос про StackOverflow: где он должен быть указан? В сигнатуре каждой функции (метода)? Операция () вызов метода автоматически должна его добавлять к каждому методу?

S>Если идти по строгому пути — то да, в сигнатуре каждой функции.

Удачи убедить хоть кого-нибудь писать на таком языке.

_>>Теперь Haskell. Имеем такой код, например:


_>>
_>>data Expr
_>>  = Val Int
_>>  | Add Expr Expr
_>>  | Mul Expr Expr
_>>  | If Expr Expr Expr

_>>eval :: Expr -> Expr
_>>eval e = ...

_>>expr0 = If (Mul (Val 1) (Val 0))
_>>           (Add (Mul (Val 4) (Val 10))
_>>                (Val 2))
_>>           (Mul (Add (Val 7) (Val 14))
_>>                (Val 2))

_>>print (eval expr0) -- Val 42
_>>


_>>Где тут должено быть указано OutOfMemory?

S>Хороший вопрос. Надо полагать — везде.

И как это должно выглядеть?

data Expr
  = Val :: Int -> (Expr | OOM)
  | Add :: Expr -> Expr -> (Expr | OOM)
  | Mul :: Expr -> Expr -> (Expr | OOM)
  ...

eval :: Expr -> (Expr | OOM)
...

expr0 = If (Mul (Val 1) (Val 0)) -- упс, (Val 1) возвращает (Expr | OOM), а Mul ожидает чистый Expr
        ...

print (eval expr0) -- и снова: expr0 у нас имеет тип (Expr | OOM), а eval ожидает чистый Expr


Попробуем по-другому:

data Expr
  = Val :: (Int | OOM) -> (Expr | OOM)
  | Add :: (Expr | OOM) -> (Expr | OOM) -> (Expr | OOM)
  | Add :: (Expr | OOM) -> (Expr | OOM) -> (Expr | OOM)
  ...

eval :: (Expr | OOM) -> (Expr | OOM)
...

expr0 = If (Mul (Val 1) (Val 0))
        ...

-- ну и print, конечно, должен иметь сигнатуру вида
-- print :: Show a => (a | OOM) -> (IO () | OOM)


То есть фактически OOM придётся писать буквально везде. Добавим сюда также StackOverflow, DivisionByZero и чёрт знает что ещё.

Удачи убедить хоть кого-нибудь писать на таком языке, да и самому не свихнуться от такого.
Re[9]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 10.01.25 06:32
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

S>>Это очень странный ход рассуждений. Во-первых, с тем же успехом можно сказать, что они были под влиянием Pascal — ведь там тоже нет checked exceptions.


PD>...Вот в Delphi точно были.


Не были.
Re[10]: Result objects - все-таки победили Exceptions?
От: Pavel Dvorkin Россия  
Дата: 10.01.25 07:16
Оценка:
Здравствуйте, korvin_, Вы писали:


PD>>...Вот в Delphi точно были.


_>Не были.


http://www.delphibasics.ru/Except.php
With best regards
Pavel Dvorkin
Re[11]: Result objects - все-таки победили Exceptions?
От: korvin_  
Дата: 10.01.25 07:45
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

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



PD>>>...Вот в Delphi точно были.


_>>Не были.


PD>http://www.delphibasics.ru/Except.php


Где здесь сказано, что они проверяемые?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.