Комментарии 16
Это выглядит просто как тип-сумма: string | failure
по аналогии с другим таким же типом string | null
. Ну или left | right
как у Either. На этом сходства по-моему заканчиваются.
Монада это свойство не типа, а операций над значениями — она требует соблюдения вполне определенных правил, а из статьи совершенно не очевидно, что именно это имелось ввиду. Скорее всего это не так потому, что ни каких операций в явном виде не предлагается, а примеры с присваиванием выглят чисто императивными.
Вообще монады интересны в функциональных языках. Увлечение разными Optional в императивных было несколько лет назад, но видимых преимуществ на практике они не дают. В лучшем случае получается полюс-минус то же самое, что с исключениями. В худшем становится только сложнее, если Optional начинает использоваться как тип аргументов.
Вот мне интересно, на основании чего делаются такие общие выводы? Я вот давно уже, скажем так — много лет, применяю такой подход в разработке, когда вместо исключений возвращается Try или Either — и у меня строго положительный опыт, потому что сигнатуры перестали замусориваться исключениями, а еще я всегда вижу, что функция может что-то и не вернуть, потому что там не просто A, а Option[A], например.
На мой взгляд, программирование вообще во многом о том, как собирать программу из кусков, т.е. о композиции. Так вот — композиция в таком виде просто намного удобнее, а еще типы компилятор проверяет и IDE подсказывает. И это еще какое преимущество. Да, мозги надо слегка разворачивать, поначалу не всем понятно, с чем это едят. Но в долгую как раз преимущества для меня лично очевидны.
Чем Optional в сигнатурах принципиально лучше исключений?
Чем это лучше в сигнатурах? Тем что в цепочке вычислений вы всегда в каждой точке знаете тип, ну, при условии конечно, что в языке вообще есть типы. Ну т.е. в какой-то момент он может перестать быть Optional[A], и станет просто A, если вы сделали get. Или стать Optional[B], если над ним проделали map A->B, и мы это вычислим, IDE это подскажет (даже для языка типа Java, система типов которого не слишком хороша). А в случае пути обработки ошибок — функция обработчик получает эту самую ошибку, то есть левую половину Either.
Для исключений такое не прокатывает — ну или никто не придумал, как это сделать.
Ну т.е., я готов согласиться что это не является серебряной пулей, и могу допустить другое мнение или другой личный опыт — но хорошо бы с примерами.
Композиция полезна для данных. Ошибки, как правило, между собой не композятся (я знаю только один пример где это может быть не совсем так — Validation). Поэтому нет причин все усложнять, смешивая их в одну кучу. Optional это такой же признак плохого дизайна как и Nullable. В тех случаях, когда это действительно необходимо, код с исключениями, где happy path и sad path явным образом разделены по-моему проще и для понимания, и для отладки.
Ничего нового вы не придумали, подобное fault-значение в Хаскеле и в ФП вообще называется "bottom" (⊥).
А еще есть монада Either/Result, которая "работает" схожим образом.
По повобду int и математики отдельная тема: можно вернуть переменную бОльшего диапазона приобразуя побитово, например не int а long, где дополнительные биты могут содержать код ошибки. Если кодировать ошибки отрицательными long, то проверку можно выполнить просто как long<0. При этом если нет ошибки, то даже отрицательный int даст положительный long, так как знаковый бит int окажется посередине long, а не в позиции знакового бита long. Как то так, но что то я не припомню где бы это было надо на практике…
Много лет назад, когда делал свой язык (как хобби), имел похожий концепт — failable-типы, хранящие или значение, или ошибку. Имел синтаксис как у nullable-типов в C#: int? Можно было спросить — это значение или ошибка. И они тоже шли параллельно исключениям (т.е. ошибкам программиста, вроде index out of bounds). Т.е. это само собой напрашивается.
У меня не было специальных операторов, язык просто автогенерил методы класса.
Но в языке не было дженериков или algebraic data types. По идее это можно решить через них и не добавлять специальный сахар.
Предлагаю концепцию Faultable type — модификацию типов по аналогии с nullable.
Предлагаю использовать сумм-типы и не добавлять в ядро языка костыли для каждой конкретной монады.
Концепция: Faultable types