52700.fb2
:: IO Int
readConfig :: String -> IO Config
:: Show a => a -> IO ()
-- большая и сложная, но !чистая! функция
algorithm
:: Int -> Config -> Result
Функция readInit читает начальное значение, функция readConfig читает из файла наcтройки, функ-
ция print выводит значение на экран, если это значение можно преобразовать в строку. Функция algorithm
это большая функция, которая вычисляет какие-то данные. Фактически наше программа это и есть функция
algorithm. В этой схеме мы добавили взаимодействие с пользователем лишь в одном месте, вся функция
algorithm построена по правилам мира описаний. Так мы внесли порядок выполнения в программу, сохра-
нив возможность определения чистых функций.
Если у нас будет ещё один “кадр”, ещё одно действие, например как только функция algorithm закончила
вычисления ей нужны дополнительные данные от пользователя, на основе которых мы сможем продолжить
вычисления с помощью какой-нибудь другой функции. Тогда наша программа примет вид:
program =
liftA2 algorithm2 readInit
(liftA2 algorithm1 readInit (readConfig ”file”))
-- функции с побочными эффектами
readInit
:: IO Int
readConfig :: String -> IO Config
:: Show a => a -> IO ()
-- большие и сложные, но !чистые! функции
algorithm1
:: Int -> Config -> Result1
algorithm2
:: Int -> Result1 -> Result2
Теперь у нас два кадра, программа выполняется в два этапа. Каждый из них разделён участками взаимо-
действия с пользователем. Но тип IO присутствует лишь в первых шести строчках, остальные два миллиона
строк написаны в мире описаний, исключительно чистыми функциями, которые поднимаются в мир специ-
альных функций с помощью функций liftA2 и стыкуются с помощью операции связывания >>=.
Попробуем тип IO в интерпретаторе. Мы будем пользоваться двумя стандартными функциями getChar и
-- читает символ с клавиатуры
getChar :: IO Char
-- выводит значение на экран
print :: IO ()
128 | Глава 8: IO
Функция print возвращает значение единичного типа, завёрнутое в тип IO, поскольку нас интересует не
само значение а побочный эффект, который выполняет эта функция, в данном случае это вывод на экран.
Закодируем два примера из первого раздела. В первом мы читаем один символ и печатаем его дважды:
Prelude> :m Control.Applicative
Prelude Control.Applicative> let res = (\c -> c:c:[]) <$> getChar >>= print
Prelude Control.Applicative> res
q”qq”