Если под «чистым» вы подразумеваете «ссылочно-прозрачный», то есть то, что прикладная функция свободно взаимозаменяема с ее оцененным результатом (и, следовательно, что вызов функции с одинаковыми аргументами каждый раз имеет один и тот же результат), любая концепция IO с состоянием в значительной степени исключен по определению.
Есть две грубые стратегии, о которых я знаю:
Пусть функция выполняет IO, но убедитесь, что она никогда не будет вызвана дважды с одинаковыми аргументами; это обходит проблему, позволяя функциям быть тривиально «ссылочно прозрачными».
Рассматривать всю программу как единственную чистую функцию, принимающую «весь полученный вход» в качестве аргумента и возвращающую «весь полученный результат», причем оба представлены в виде ленивого потока для обеспечения интерактивности.
Существует множество способов реализации обоих подходов, а также некоторая степень перекрытия - например, во втором случае функции, работающие с потоками ввода / вывода, вряд ли будут вызываться дважды с одной и той же частью поток. Какой способ взглянуть на это имеет больше смысла, зависит от того, какую поддержку вам оказывает язык.
В Haskell IO
- это тип монады, который автоматически пропускает последовательное состояние через код (аналогично функционально чистой монаде State
), так что, концептуально, каждый вызов нечистой функции получает различное значение неявного "состояния внешнего мира".
Другой популярный подход, о котором я знаю, использует что-то вроде линейных типов для аналогичной цели; гарантируя, что нечистые функции никогда не получат одинаковые аргументы дважды, имея значения, которые не могут быть скопированы или дублированы, так что старые значения «состояния внешнего мира» не могут быть сохранены и использованы повторно.