Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
data Maybe a = Nothing | Just a
f::Maybe a -> a
, выдав значение по умолчанию для Nothing. Это чистые значения.f::IO String
действительно может быть какой угодно альтернативой в IO
, о которой мы ничего не знаем. Это может быть прочтенная строка с консоли, ошибка, что угодно. Заметим, что мы даже не можем предполагать, что значение с типом IO String
это действительно какая то альтернатива IO, нет это всегда одно значение, но оно «многолико». Именно поэтому getLine::IO String
это значение (функция без аргументов), которое может «прочесть любую строку с консоли» — прочтенная строка будет строкой/ошибкой/etc для наблюдателя (наблюдатель не находиться в «чистом» мире), в самой программе оперируется чистое значение «все возможные варианты IO» (ключевые: суперпозиция, посыл к квантовой физике)IO
или в любом другом «неизвестном типе»/монаде, значит нельзя что либо сделать со значением IO a
. Для этого и есть оператор применения >>=, который достает а из монадного значения и применяет ее к функции f::a -> IO a
если это возможно. Иначе (если к примеру IO String
был ошибкой, а не прочитанной строкой) возвращает новое значение IO a, о котором мы ничего не знаем. Именно потому, что >>= может вернуть IO a
«в обход» нашей f, f должна возвращать IO a
. Т.е. однажды «испачкавшись» в монадах вы увязли до самого конца программы. В функциональной программе нет времени, т.е. нельзя стать наблюдателем и посмотреть что там в монадном значении (unsafe функции, те которые при «исключительных» значениях «не знают что делать» и оставлены на совесть программисту, мы не рассматриваем. это хак малодушных :), это не Haskell), значит нельзя избавиться от монада. На самом деле программа на Haskell всегда, не зависимо от времени, окружающего мира и т.д. отдает IO ()
, «многоликое» неизвестное значение, и завершается, мгновенно. После работы программы мы в своем мире с side-effects наблюдаем за полученным значением с типом IO ()
и видим один из «выпавших» вариантов: вывод на консоль, окошки и т.д. Даже если программа работает несколько суток, общается с пользователем и выкачивает чего из интернета, все равно в «чистом мире» Haskell программа уже отработала и вернула нам IO ()
со всем что мы видим, можем ввести и можем получить результат обратно. Там нет времени, там нет side-effects :)hasFailed::IO а -> Bool
. Но это совсем не хорошо, т.к. из всего «бесконечного» множества вариантов IO a
мы вдруг получили Bool, который всегда True | False, не зависимо от действий внешнего мира и времени. Таких функций быть не должно, это unsafe функции. Но может быть hIsClosed::Handle -> IO Bool
, применяемая к IO Handle
. Тогда логика скрыта в Handle, если там не что-то еще из бесконечного множество «чего то еще». Получил IO Bool
мы можем сделать свою логику f::Bool -> IO SomethingElse
или дальше катиться в потоке IO SomethingElse. Это может сносить немного крышу. Что бы это понять попробуйте представить, что ваша программа может дать верные результаты или выпасть с ошибками — это все «известные» результаты; либо уборщица шваброй выдернет питание и все накроется — это «не известные» результаты, одно из не подвластных вам проявлений IO SomethingElse
. Функциональный мир при этом остается чист и детерминирован.getLine::IO String
это всегда одно и то же значение типа IO String
, не зависимо от времени или каких либо других сторонних факторов. putStr::String -> IO()
всегда возвращает одно и тоже значение типа IO ()
для одной и той же строки. Выражение:(getLine >>= putStr)::IO ()
Еще Одно Руководство по Монадам (часть 3: Монадные Законы)