Re[3]: Result objects - все-таки победили Exceptions?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.01.25 09:39
Оценка: 1 (1) +1
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>В современных IDE нет никакой проблемы, чтобы указать. IDEA просто не даст откомпилировать метод, в котором выбрасывается checked исключение, потребует либо поставить try-catch, либо добавить throw, и сама это сделает.
Примерно 25 лет тому назад парни из Редмонда проделали простое упражнение: посмотрели на кодовую базу реальных проектов на Java.
И внезапно оказалось, что почти все абьюзят checked exceptions. Они прекрасно выглядят в теории, но на практике никакая IDE не помогает решить их фундаментальную проблему: они запечены навечно в сигнатурах интерфейсов.

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

Середина этого дерева знать не знает про эту специфическую ситуацию; её спроектировали в те времена, когда никому и в голову не приходило, что там внутри может что-то сломаться таким способом.
В мире checked exceptions у нас есть три плохих способа это решить:
1. Пройтись вверх по стеку вызовов и везде подправить сигнатуры, добавив MySpecificException к списку
Это зачастую невозможно (потому что в стеке — четыре библиотеки от семи разных поставщиков, из которых девятеро уже давно ушли с рынка и их код никто не маинтейнит), да и вредно (потому что часть этих библиотек используется ещё в сорока местах нашего проекта, которым совершенно неинтересна обязанность обрабатывать ещё и MySpecificException)
2. Отнаследоваться от какого-то исключения, которое в сигнатурах уже есть
Это если в сигнатурах есть хоть что-то, и это что-то — не final.
3. Завернуть MySpecificException в UniversalException
Это если в сигнатурах есть UniversalException, в котором предусмотрено место для InnerException
4. Выбросить потомка RuntimeException

В итоге практически реализуемым становится четвёртый способ, который сводит всю идею проверяемых исключений на нет.
Именно поэтому в дотнете нету checked exceptions.

А вот если идти по пути "result object", то мы остаёмся в поле традиционной системы типов.
Вот есть у нас библиотечная функция map, которая принимает коллекцию и трансформер f вида Func<T, R>. Нам совершенно всё равно, может ли возникнуть ошибка при вычислении f над конкретным экземпляром T или нет.
Потому что всё это "спрятано" внутри типа R. Если f — это функция 1/x, то она возвращает тип "number | undefined" (или там "number | DivisionByZero"). Тип результата map соответственно будет Iterable<number | undefined> и обязанность принять решение, что с этим делать, остаётся у того, кто вызывает функцию map, а не у автора этой функции. Ровно так, как этого хотели авторы идеи исключений.
А если f — это функция x ^ 0xF, то у неё тип результата — просто number, и вызов map с ней в качестве аргумента получит Iterable<number>.
Написать функцию IEnumerable<R> map(IEnumerable<T> source, Func<T, R> f) c проверяемыми исключениями не представляется возможным. В ней функция f не имеет права ничего выбрасывать, потому что map пообещала ничего не выбрасывать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.