Сообщение Re[6]: Result objects - все-таки победили Exceptions? от 09.01.2025 14:54
Изменено 09.01.2025 15:16 Pavel Dvorkin
Re[6]: Result objects - все-таки победили Exceptions?
Здравствуйте, Sinclair, Вы писали:
PD>>Мы же пишем код очень низкий по стеку вызовов.
S>Мы — это кто?
Ты. В своем примере с деревом.
>В реальном ентерпрайз-коде типичная глубина стек-трейса — пара сотен фреймов.
Это да
PD>>А это чаще всего означает, что мы вкладываем некую новую функциональность, которая изначально не была заложена во всех этих четырех библиотеках от семи разных источников. Ну например, работа с данными, которые поступают из файлов, которые лежат в какой-то встраиваемой ФС, а мы решили встроить новую ФС, а в ней нам нужно завести какое-то исключение, которого ни в одной из существующих ФС никогда не было. Такая уж странная ФС.
S>Ничего странного. Например, предкам не приходило в голову, что данные могут лежать не на диске, а в клауде.
Возможно. Подходящий пример, да.
PD>>Если его и впрямь нельзя уложить в одно из исключений ранее существовавших ФС, то это значит, что мы работаем с чем-то принципиально новым. И тогда понятно, что прежний код (все четыре библиотеки от семи источников), в общем-то, не рассчитан на это нововведение. И обработать эту ошибку он не может (вариант с его переписыванием в виде (1) не обсуждаем). А вообще-то должен бы именно он это делать. А мы фактически хотим пробросить эту ошибку, минуя все слои, которые могли бы ее обработать, на один из верхних уровней, где обработать ее по существу едва ли удастся, удастся лишь признать неудачу.
S>С чего бы это? На самом верху — наш код, он знает, чем retryable HTTP error отличаются от non-retryable.
S>в самом низу — тоже наш код, он как раз порождает разные виды исключений, в зависимости от кода HTTP-респонса.
S>А вот в середине — треш, угар и содомия возрастом 30+.
Внизу у нас (в рамках примера с cloud) код, который работает с этой cloud file system, и знать про HTTP ничего не должен. Он чего-то из этой ФС берет и у него иногда не получается. А потом отдает выше и там треш и угар делают из этого HTTP какой-то, или же HTTP error хоть retryable, хоть нет
PD>>В самом деле, ну поймали мы ее там, и что дальше ? Разве что повторить операцию с какими-то изменениями... А это и значит, что возникло исключение, которого код не ждал. То есть фактически unchecked exception. И тут действительно можно выбросить RuntimeException.
S>Ну, так проблема — в том, что компилятор теперь нам ничего не говорит о том, где это исключение нужно ловить. Нам показалось, что мы его перехватили, а на практике оно вылетело куда-то вовсе не туда, где мы поставили catch.
S>Это и есть подрыв всей идеологии checked exceptions.
Безусловно не туда, потому что туда не получится. Туда — это в треш и угар, там бы его и должны были бы обработать и сделать HTTP иным способом, может быть. Или HTTP error сделать. И это не зависит от того, checked или unchecked — мы этот треш и угар по условиям править не можем, а именно он HTTP error делает и верхнему уровню возвращает, а тот уж и решает, что потом с ним делать. А нижний уровень с файловой системой работает и знать про HTTP ничего не может.
PD>>Подозреваю, что не поэтому, а потому что унаследовались от C++, а в нем тогда (не знаю как сейчас) throws в сигнатуре метода ровным счетом ничего не делало. Так, для красоты...
S>Нет. Никто там от С++ не наследовался. И подозревать причины нет — все ходы записаны, включая ход мысли отцов-основателей.
Допускаю. Равно как допускаю, что они неосознанно все же были под влиянием MSVC++. Не могли они его не знать.
PD>>А с uncheked exception другая проблема. Я с ней именно на C# когда-то столкнулся. Все сделано по документации, все работает, и вдруг вылетает XyzException. В документации ни слова о том, что оно может быть выброшено. Да это и неудивительно, так как оно выбрасывается не вызываемым методом самим, а методом третьей из 4 библиотек семи разработчиков, о которых вызываемый метод и не знает ничего. Ну ладно, чертыхнулся, написал для него catch (хорошо, хоть было понятно, что за исключение и что делать) и задумался — а какие еще исключения этот метод может выбросить не сам, а в одной из этих 4 библиотек ? Дай ответ... Не дает ответа.
S>В Java у вас будет ровно то же самое. Как раз потому, что в сигнатуре ничего нет, и вдруг вылетает какой-то XyzException, отнаследованный от RuntimeException.
Тут будет, да. Но это особая ситуация. Произошло что-то непредвиденное. На все такие непредвиденные ситуации не напишешь throws. В конце концов NullPointerException может почти везде возникнуть
, не будешь же его писать в каждом методе.
А вот "штатные" ошибки под контролем. Например, отсутствие файла, ошибка IO и все другие. Я не смогу написать код, который не учтет возможности их возникновения, мне просто не дадут это сделать. Мне придется решить, что я буду делать, если такая проблема возникнет. И это правильно. Если я пишу метод, читающий строки из файла, я должен написать код, который их читает и код, который что-то делает, если файла нет. Либо сам делает, либо декларирует — я не могу это обработать, оставляю обработку вызывающему коду. Четко и ясно. Входит в контракт метода.
PD>>Мы же пишем код очень низкий по стеку вызовов.
S>Мы — это кто?
Ты. В своем примере с деревом.
>В реальном ентерпрайз-коде типичная глубина стек-трейса — пара сотен фреймов.
Это да
PD>>А это чаще всего означает, что мы вкладываем некую новую функциональность, которая изначально не была заложена во всех этих четырех библиотеках от семи разных источников. Ну например, работа с данными, которые поступают из файлов, которые лежат в какой-то встраиваемой ФС, а мы решили встроить новую ФС, а в ней нам нужно завести какое-то исключение, которого ни в одной из существующих ФС никогда не было. Такая уж странная ФС.
S>Ничего странного. Например, предкам не приходило в голову, что данные могут лежать не на диске, а в клауде.
Возможно. Подходящий пример, да.
PD>>Если его и впрямь нельзя уложить в одно из исключений ранее существовавших ФС, то это значит, что мы работаем с чем-то принципиально новым. И тогда понятно, что прежний код (все четыре библиотеки от семи источников), в общем-то, не рассчитан на это нововведение. И обработать эту ошибку он не может (вариант с его переписыванием в виде (1) не обсуждаем). А вообще-то должен бы именно он это делать. А мы фактически хотим пробросить эту ошибку, минуя все слои, которые могли бы ее обработать, на один из верхних уровней, где обработать ее по существу едва ли удастся, удастся лишь признать неудачу.
S>С чего бы это? На самом верху — наш код, он знает, чем retryable HTTP error отличаются от non-retryable.
S>в самом низу — тоже наш код, он как раз порождает разные виды исключений, в зависимости от кода HTTP-респонса.
S>А вот в середине — треш, угар и содомия возрастом 30+.
Внизу у нас (в рамках примера с cloud) код, который работает с этой cloud file system, и знать про HTTP ничего не должен. Он чего-то из этой ФС берет и у него иногда не получается. А потом отдает выше и там треш и угар делают из этого HTTP какой-то, или же HTTP error хоть retryable, хоть нет
PD>>В самом деле, ну поймали мы ее там, и что дальше ? Разве что повторить операцию с какими-то изменениями... А это и значит, что возникло исключение, которого код не ждал. То есть фактически unchecked exception. И тут действительно можно выбросить RuntimeException.
S>Ну, так проблема — в том, что компилятор теперь нам ничего не говорит о том, где это исключение нужно ловить. Нам показалось, что мы его перехватили, а на практике оно вылетело куда-то вовсе не туда, где мы поставили catch.
S>Это и есть подрыв всей идеологии checked exceptions.
Безусловно не туда, потому что туда не получится. Туда — это в треш и угар, там бы его и должны были бы обработать и сделать HTTP иным способом, может быть. Или HTTP error сделать. И это не зависит от того, checked или unchecked — мы этот треш и угар по условиям править не можем, а именно он HTTP error делает и верхнему уровню возвращает, а тот уж и решает, что потом с ним делать. А нижний уровень с файловой системой работает и знать про HTTP ничего не может.
PD>>Подозреваю, что не поэтому, а потому что унаследовались от C++, а в нем тогда (не знаю как сейчас) throws в сигнатуре метода ровным счетом ничего не делало. Так, для красоты...
S>Нет. Никто там от С++ не наследовался. И подозревать причины нет — все ходы записаны, включая ход мысли отцов-основателей.
Допускаю. Равно как допускаю, что они неосознанно все же были под влиянием MSVC++. Не могли они его не знать.
PD>>А с uncheked exception другая проблема. Я с ней именно на C# когда-то столкнулся. Все сделано по документации, все работает, и вдруг вылетает XyzException. В документации ни слова о том, что оно может быть выброшено. Да это и неудивительно, так как оно выбрасывается не вызываемым методом самим, а методом третьей из 4 библиотек семи разработчиков, о которых вызываемый метод и не знает ничего. Ну ладно, чертыхнулся, написал для него catch (хорошо, хоть было понятно, что за исключение и что делать) и задумался — а какие еще исключения этот метод может выбросить не сам, а в одной из этих 4 библиотек ? Дай ответ... Не дает ответа.
S>В Java у вас будет ровно то же самое. Как раз потому, что в сигнатуре ничего нет, и вдруг вылетает какой-то XyzException, отнаследованный от RuntimeException.
Тут будет, да. Но это особая ситуация. Произошло что-то непредвиденное. На все такие непредвиденные ситуации не напишешь throws. В конце концов NullPointerException может почти везде возникнуть
А вот "штатные" ошибки под контролем. Например, отсутствие файла, ошибка IO и все другие. Я не смогу написать код, который не учтет возможности их возникновения, мне просто не дадут это сделать. Мне придется решить, что я буду делать, если такая проблема возникнет. И это правильно. Если я пишу метод, читающий строки из файла, я должен написать код, который их читает и код, который что-то делает, если файла нет. Либо сам делает, либо декларирует — я не могу это обработать, оставляю обработку вызывающему коду. Четко и ясно. Входит в контракт метода.
Re[6]: Result objects - все-таки победили Exceptions?
Здравствуйте, Sinclair, Вы писали:
PD>>Мы же пишем код очень низкий по стеку вызовов.
S>Мы — это кто?
Ты. В своем примере с деревом.
>В реальном ентерпрайз-коде типичная глубина стек-трейса — пара сотен фреймов.
Это да
PD>>А это чаще всего означает, что мы вкладываем некую новую функциональность, которая изначально не была заложена во всех этих четырех библиотеках от семи разных источников. Ну например, работа с данными, которые поступают из файлов, которые лежат в какой-то встраиваемой ФС, а мы решили встроить новую ФС, а в ней нам нужно завести какое-то исключение, которого ни в одной из существующих ФС никогда не было. Такая уж странная ФС.
S>Ничего странного. Например, предкам не приходило в голову, что данные могут лежать не на диске, а в клауде.
Возможно. Подходящий пример, да.
PD>>Если его и впрямь нельзя уложить в одно из исключений ранее существовавших ФС, то это значит, что мы работаем с чем-то принципиально новым. И тогда понятно, что прежний код (все четыре библиотеки от семи источников), в общем-то, не рассчитан на это нововведение. И обработать эту ошибку он не может (вариант с его переписыванием в виде (1) не обсуждаем). А вообще-то должен бы именно он это делать. А мы фактически хотим пробросить эту ошибку, минуя все слои, которые могли бы ее обработать, на один из верхних уровней, где обработать ее по существу едва ли удастся, удастся лишь признать неудачу.
S>С чего бы это? На самом верху — наш код, он знает, чем retryable HTTP error отличаются от non-retryable.
S>в самом низу — тоже наш код, он как раз порождает разные виды исключений, в зависимости от кода HTTP-респонса.
S>А вот в середине — треш, угар и содомия возрастом 30+.
Внизу у нас (в рамках примера с cloud) код, который работает с этой cloud file system, и знать про HTTP ничего не должен. Он чего-то из этой ФС берет и у него иногда не получается. А потом отдает выше и там треш и угар делают из этого HTTP какой-то, или же HTTP error хоть retryable, хоть нет
PD>>В самом деле, ну поймали мы ее там, и что дальше ? Разве что повторить операцию с какими-то изменениями... А это и значит, что возникло исключение, которого код не ждал. То есть фактически unchecked exception. И тут действительно можно выбросить RuntimeException.
S>Ну, так проблема — в том, что компилятор теперь нам ничего не говорит о том, где это исключение нужно ловить. Нам показалось, что мы его перехватили, а на практике оно вылетело куда-то вовсе не туда, где мы поставили catch.
S>Это и есть подрыв всей идеологии checked exceptions.
Безусловно не туда, потому что туда не получится. Туда — это в треш и угар, там бы его и должны были бы обработать и сделать HTTP иным способом, может быть. Или HTTP error сделать. И это не зависит от того, checked или unchecked — мы этот треш и угар по условиям править не можем, а именно он HTTP error делает и верхнему уровню возвращает, а тот уж и решает, что потом с ним делать. А нижний уровень с файловой системой работает и знать про HTTP ничего не может.
PD>>Подозреваю, что не поэтому, а потому что унаследовались от C++, а в нем тогда (не знаю как сейчас) throws в сигнатуре метода ровным счетом ничего не делало. Так, для красоты...
S>Нет. Никто там от С++ не наследовался. И подозревать причины нет — все ходы записаны, включая ход мысли отцов-основателей.
Допускаю. Равно как допускаю, что они неосознанно все же были под влиянием MSVC++. Не могли они его не знать.
PD>>А с uncheked exception другая проблема. Я с ней именно на C# когда-то столкнулся. Все сделано по документации, все работает, и вдруг вылетает XyzException. В документации ни слова о том, что оно может быть выброшено. Да это и неудивительно, так как оно выбрасывается не вызываемым методом самим, а методом третьей из 4 библиотек семи разработчиков, о которых вызываемый метод и не знает ничего. Ну ладно, чертыхнулся, написал для него catch (хорошо, хоть было понятно, что за исключение и что делать) и задумался — а какие еще исключения этот метод может выбросить не сам, а в одной из этих 4 библиотек ? Дай ответ... Не дает ответа.
S>В Java у вас будет ровно то же самое. Как раз потому, что в сигнатуре ничего нет, и вдруг вылетает какой-то XyzException, отнаследованный от RuntimeException.
Тут будет, да. Но это особая ситуация. Произошло что-то непредвиденное. На все такие непредвиденные ситуации не напишешь throws. В конце концов NullPointerException может почти везде возникнуть
, не будешь же его писать в каждом методе.
А вот "штатные" ошибки под контролем. Например, отсутствие файла, ошибка IO и все другие. Я не смогу написать код, который не учтет возможности их возникновения, мне просто не дадут это сделать. Мне придется решить, что я буду делать, если такая проблема возникнет. И это правильно. Если я пишу метод, читающий строки из файла, я должен написать код, который их читает и код, который что-то делает, если файла нет. Либо сам делает, либо декларирует — я не могу это обработать, оставляю обработку вызывающему коду. Четко и ясно. Входит в контракт метода.
Кстати. Кроме твоих 4 вариантов, есть и 5-й. Выбросить-таки RuntimeException, но завернуть в него этот самый XyZException. На нижнем уровне его генерируют, на верхнем должны теперь понимать, коль скоро на верхнем мы его теперь хотим обрабатывать. А треш и угар все это пройдет как нож через масло.
PD>>Мы же пишем код очень низкий по стеку вызовов.
S>Мы — это кто?
Ты. В своем примере с деревом.
>В реальном ентерпрайз-коде типичная глубина стек-трейса — пара сотен фреймов.
Это да
PD>>А это чаще всего означает, что мы вкладываем некую новую функциональность, которая изначально не была заложена во всех этих четырех библиотеках от семи разных источников. Ну например, работа с данными, которые поступают из файлов, которые лежат в какой-то встраиваемой ФС, а мы решили встроить новую ФС, а в ней нам нужно завести какое-то исключение, которого ни в одной из существующих ФС никогда не было. Такая уж странная ФС.
S>Ничего странного. Например, предкам не приходило в голову, что данные могут лежать не на диске, а в клауде.
Возможно. Подходящий пример, да.
PD>>Если его и впрямь нельзя уложить в одно из исключений ранее существовавших ФС, то это значит, что мы работаем с чем-то принципиально новым. И тогда понятно, что прежний код (все четыре библиотеки от семи источников), в общем-то, не рассчитан на это нововведение. И обработать эту ошибку он не может (вариант с его переписыванием в виде (1) не обсуждаем). А вообще-то должен бы именно он это делать. А мы фактически хотим пробросить эту ошибку, минуя все слои, которые могли бы ее обработать, на один из верхних уровней, где обработать ее по существу едва ли удастся, удастся лишь признать неудачу.
S>С чего бы это? На самом верху — наш код, он знает, чем retryable HTTP error отличаются от non-retryable.
S>в самом низу — тоже наш код, он как раз порождает разные виды исключений, в зависимости от кода HTTP-респонса.
S>А вот в середине — треш, угар и содомия возрастом 30+.
Внизу у нас (в рамках примера с cloud) код, который работает с этой cloud file system, и знать про HTTP ничего не должен. Он чего-то из этой ФС берет и у него иногда не получается. А потом отдает выше и там треш и угар делают из этого HTTP какой-то, или же HTTP error хоть retryable, хоть нет
PD>>В самом деле, ну поймали мы ее там, и что дальше ? Разве что повторить операцию с какими-то изменениями... А это и значит, что возникло исключение, которого код не ждал. То есть фактически unchecked exception. И тут действительно можно выбросить RuntimeException.
S>Ну, так проблема — в том, что компилятор теперь нам ничего не говорит о том, где это исключение нужно ловить. Нам показалось, что мы его перехватили, а на практике оно вылетело куда-то вовсе не туда, где мы поставили catch.
S>Это и есть подрыв всей идеологии checked exceptions.
Безусловно не туда, потому что туда не получится. Туда — это в треш и угар, там бы его и должны были бы обработать и сделать HTTP иным способом, может быть. Или HTTP error сделать. И это не зависит от того, checked или unchecked — мы этот треш и угар по условиям править не можем, а именно он HTTP error делает и верхнему уровню возвращает, а тот уж и решает, что потом с ним делать. А нижний уровень с файловой системой работает и знать про HTTP ничего не может.
PD>>Подозреваю, что не поэтому, а потому что унаследовались от C++, а в нем тогда (не знаю как сейчас) throws в сигнатуре метода ровным счетом ничего не делало. Так, для красоты...
S>Нет. Никто там от С++ не наследовался. И подозревать причины нет — все ходы записаны, включая ход мысли отцов-основателей.
Допускаю. Равно как допускаю, что они неосознанно все же были под влиянием MSVC++. Не могли они его не знать.
PD>>А с uncheked exception другая проблема. Я с ней именно на C# когда-то столкнулся. Все сделано по документации, все работает, и вдруг вылетает XyzException. В документации ни слова о том, что оно может быть выброшено. Да это и неудивительно, так как оно выбрасывается не вызываемым методом самим, а методом третьей из 4 библиотек семи разработчиков, о которых вызываемый метод и не знает ничего. Ну ладно, чертыхнулся, написал для него catch (хорошо, хоть было понятно, что за исключение и что делать) и задумался — а какие еще исключения этот метод может выбросить не сам, а в одной из этих 4 библиотек ? Дай ответ... Не дает ответа.
S>В Java у вас будет ровно то же самое. Как раз потому, что в сигнатуре ничего нет, и вдруг вылетает какой-то XyzException, отнаследованный от RuntimeException.
Тут будет, да. Но это особая ситуация. Произошло что-то непредвиденное. На все такие непредвиденные ситуации не напишешь throws. В конце концов NullPointerException может почти везде возникнуть
А вот "штатные" ошибки под контролем. Например, отсутствие файла, ошибка IO и все другие. Я не смогу написать код, который не учтет возможности их возникновения, мне просто не дадут это сделать. Мне придется решить, что я буду делать, если такая проблема возникнет. И это правильно. Если я пишу метод, читающий строки из файла, я должен написать код, который их читает и код, который что-то делает, если файла нет. Либо сам делает, либо декларирует — я не могу это обработать, оставляю обработку вызывающему коду. Четко и ясно. Входит в контракт метода.
Кстати. Кроме твоих 4 вариантов, есть и 5-й. Выбросить-таки RuntimeException, но завернуть в него этот самый XyZException. На нижнем уровне его генерируют, на верхнем должны теперь понимать, коль скоро на верхнем мы его теперь хотим обрабатывать. А треш и угар все это пройдет как нож через масло.