Pull to refresh

Comments 16

Это выглядит просто как тип-сумма: string | failure по аналогии с другим таким же типом string | null. Ну или left | right как у Either. На этом сходства по-моему заканчиваются.


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


Вообще монады интересны в функциональных языках. Увлечение разными Optional в императивных было несколько лет назад, но видимых преимуществ на практике они не дают. В лучшем случае получается полюс-минус то же самое, что с исключениями. В худшем становится только сложнее, если Optional начинает использоваться как тип аргументов.

>В лучшем случае получается полюс-минус то же самое, что с исключениями.
Вот мне интересно, на основании чего делаются такие общие выводы? Я вот давно уже, скажем так — много лет, применяю такой подход в разработке, когда вместо исключений возвращается Try или Either — и у меня строго положительный опыт, потому что сигнатуры перестали замусориваться исключениями, а еще я всегда вижу, что функция может что-то и не вернуть, потому что там не просто A, а Option[A], например.

На мой взгляд, программирование вообще во многом о том, как собирать программу из кусков, т.е. о композиции. Так вот — композиция в таком виде просто намного удобнее, а еще типы компилятор проверяет и IDE подсказывает. И это еще какое преимущество. Да, мозги надо слегка разворачивать, поначалу не всем понятно, с чем это едят. Но в долгую как раз преимущества для меня лично очевидны.

Чем Optional в сигнатурах принципиально лучше исключений?

Погодите, вопрос без подколки — Optional не имеет отношения к исключениям. Исключения — это либо Either, либо Try — который его частный случай по факту. Вы точно пробовали это применять?

Чем это лучше в сигнатурах? Тем что в цепочке вычислений вы всегда в каждой точке знаете тип, ну, при условии конечно, что в языке вообще есть типы. Ну т.е. в какой-то момент он может перестать быть 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. По идее это можно решить через них и не добавлять специальный сахар.

человек изобретает велосипед, не мешайте ))
Вот прям в язык встроить? А просто использовать pairs или свою структуру нельзя?
Можно, конечно. Но толку, если стандартная библиотека о нём не знает, не говоря уже о стандартных арифметических операторах.
Предлагаю концепцию Faultable type — модификацию типов по аналогии с nullable.

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

Почему многоэтажными? Можно на уровне стандартной либы завести тип Faultable<T> и, собственно, всё.

Sign up to leave a comment.

Articles