Поэтому, скажем, запрос к базе данных или запись файла не могут быть выполнены в чисто функциональном стиле по определению. Это, например, одна из причин, по которой нам нужны монады.
Никому не нужны монады, это всего лишь один способ описать вещи. На самом деле, это, вероятно, даже не самый лучший способ. Некоторая форма типизации эффектов , типы уникальности или система, основанная на полной линейной логике, кажутся более убедительными в теории, но все они более радикально отличаются от хорошо известных систем типов и более сложны для выражения. Монадический ввод-вывод, обнаруженный в Haskell, представляет собой компромисс между удобством использования и простотой, поскольку он по существу моделирует полностью императивное программирование таким образом, который легко сосуществовал с существующей системой типов в стиле ML, уже используемой в языке.
Вопрос в том, почему мы рассматриваем вывод STDOUT как нечто нечистое? Да, любой обработчик файлов рискован - мы никогда не можем быть уверены, что данные всегда будут записаны. Но как насчет STDOUT? Почему мы должны думать об этом как о чем-то ненадежном? Является ли более ненадежной сама оценка? Я имею в виду, мы всегда можем нажать триггер и, таким образом, вычислить прерывание.
Это не так, а мы нет. Ввод и вывод из программы в целом можно просто рассматривать как аргументы и результаты обработки всей программы как одной большой чистой функции. Пока он выводит одно и то же на стандартный вывод, если вы передаете ему то же самое с стандартного ввода, это все еще чистая функция. Фактически, до введения монадического ввода-вывода, Haskell использовал систему ввода-вывода на основе потоков, которая использовала чистые ленивые потоки для ввода и вывода. Он уронил его, потому что, по-видимому, было неудобно использовать, что может дать вам некоторое представление о том, почему вы не слышали ничего подобного. :]
Чтобы сделать это более глупо, рассмотрим минималистский эзотерический язык Lazy K :
Lazy K - это собранный мусором, ссылочно-прозрачный функциональный язык программирования с простой потоковой системой ввода-вывода.
Что отличает Lazy K от других таких языков, так это почти полное отсутствие других функций. Например, он не предлагает интегрированную систему полиморфных типов Хиндли-Милнера. Он не поставляется с обширной стандартной библиотекой с поддержкой независимого от платформы программирования GUI и привязок к другим языкам. Ни одна из таких библиотек не может быть написана, поскольку, среди прочего, Lazy K не предоставляет никакого способа определения или ссылки на какие-либо функции, кроме встроенных. Эта неспособность дополняется отсутствием соответствующей поддержки чисел, строк или любых других типов данных. Тем не менее, Lazy K завершен по Тьюрингу.
(...)
Программы Lazy K живут в том же вечном платоновском царстве, что и математические функции, что на странице Unlambda называется «благословенным царством чистого нетипизированного лямбда-исчисления». Как сборщик мусора скрывает процесс управления памятью от программиста, так и ссылочная прозрачность скрывает процесс оценки. Тот факт, что некоторые вычисления необходимы для просмотра изображения набора Мандельброта или для «запуска» программы Lazy K, является подробностью реализации. В этом суть функционального программирования.
(...)
Как обрабатывать ввод и вывод на языке без побочных эффектов? В определенном смысле ввод и вывод не являются побочными эффектами; они, так сказать, фронтальные и обратные эффекты. Так и в Lazy K, где программа просто рассматривается как функция из пространства возможных входов в пространство возможных выходов.
Я сомневаюсь, что вы найдете более чисто функциональный язык, чем этот!
Имейте в виду, однако, что вышеупомянутое применимо только к тому, чтобы по существу взять ввод и вывод чистой функции и каким-то образом подключить их к stdin / stdout «извне». Существует большая разница между этим и наличием доступа к реальным примитивам ввода-вывода системного уровня. Детали реализации чтения и записи в потоки могут не содержать примесей, если не будут тщательно инкапсулированы.
Я ожидаю, что это главная причина, по которой вы не можете сделать это напрямую в Haskell - разумные варианты использования невелики по сравнению с использованием монадического ввода-вывода, и для последнего есть много преимуществ в доступе к реальному. Я полагаю, что именно поэтому, например, аргументы командной строки для программы не просто передаются в качестве аргументов main
, хотя интуитивно кажется, что они должны быть.
Вы можете восстановить минимальную версию чего - то подобное в конкретной программе, хотя - просто захватить аргументы как чистые ценности , а затем использовать в interact
функцию для остальной части вашей программы.
Хотя чистота в функциональной программе является достойной целью, реальность такова, что каждая нетривиальная, полезная программа будет иметь некоторую нечистоту (или «побочные эффекты») по причинам, которые вы упомянули.
Полностью чистые программы, по определению, представляют собой закрытый черный ящик и по сути неинтересны.
Функциональный язык Haskell решает эту проблему, изолируя побочные эффекты, такие как вывод в монады . Монада сохраняет чисто функциональный стиль программирования, в то же время позволяя производить вывод.
источник
Запись в STDOUT может завершиться неудачно, если вы не подключены к терминальному устройству или если вы (по какой-то причине) закрыли дескриптор файла для него.
Кроме того, STDOUT не всегда является «экраном консоли». Иногда это передается в другую программу. Иногда труба сломана.
источник
Это помогает, если вы думаете о чистоте в терминах «Изменяет состояние внешнего мира». Это может включать запись в консоль, файл журнала, извлечение компакт-диска или «Запуск ракет».
Это также может быть проблемой с точки зрения одновременного выполнения. Если вы знаете, что функция не имеет побочных эффектов, вы можете легко организовать параллелизм, поскольку вы можете доказать, что не может быть никаких условий гонки или чего-либо подобного.
источник