Изучай Haskell во имя добра! (Липовача) - страница 118

– не чистая функция, потому что её результат может быть неодинаковым при последовательных вызовах. Вот почему она как бы «запачкана» конструктором типов >IO, и мы можем получить данные только внутри действий ввода-вывода, имеющих в сигнатуре типа маркёр >IO. Так как код для ввода-вывода также «испачкан», любое вычисление, зависящее от «испачканных» >IO-данных, также будет давать «грязный»результат.



Если я говорю «испачканы», это не значит, что мы не сможем использовать результат, содержащийся в типе >IO в чистом коде. Мы временно «очищаем» данные внутри действия, когда связываем их с именем. В выражении >name <– getLine образец >name содержит обычную строку, представляющую содержимое ящика.

Мы можем написать сложную функцию, которая, скажем, принимает ваше имя как параметр (обычная строка) и предсказывает вашу удачливость или будущее всей вашей жизни, основываясь на имени:

>main = do

>   putStrLn "Привет, как тебя зовут?"

>   name <– getLine

>   putStrLn $ "Вот твоё будущее: " ++ tellFortune name

Функция >tellFortune (или любая другая, которой мы передаём значение >name) не должна знать ничего про >IO – это обычная функция >String>–>>String.

Посмотрите на этот образец кода. Корректен ли он?

>nameTag = "Привет, меня зовут " ++ getLine

Если вы ответили «нет», возьмите с полки пирожок. Если ответили «да», убейте себя об стену… Шучу, не надо! Это выражение не сработает, потому что оператор >++ требует, чтобы оба параметра были списками одинакового типа. Левый параметр имеет тип >String (или >[Char], если вам угодно), в то время как функция >getLine возвращает значение типа >IO String. Вы не сможете конкатенировать строку и результат действия ввода-вывода. Для начала нам нужно извлечь результат из действия ввода-вывода, чтобы получить значение типа >String, и единственный способ сделать это – выполнить что-то вроде >name <– getLine внутри другого действия ввода-вывода. Если мы хотим работать с «нечистыми» данными, то должны делать это в «нечистом» окружении!… Итак, грязь от нечистоты распространяется как моровое поветрие, и в наших интересах делать часть для осуществления ввода-вывода настолько малой, насколько это возможно.

Каждое выполненное действие ввода-вывода заключает в себе результат. Вот почему наш предыдущий пример можно переписать так:

>main = do

>   foo <- putStrLn "Привет, как тебя зовут?"

>   name <– getLine

>   putStrLn ("Привет, " ++ name ++ ", ну ты и хипстота!")

Тем не менее образец >foo всегда будет получать значение >(), так что большого смысла в этом нет. Заметьте: мы не связываем последний вызов функции