Нечистые языки в принципе не отличаются от более привычных императивных языков, особенно сейчас, когда скопировано много функциональных уловок. Отличается стиль - как вы решаете проблемы.
Считаете ли вы Haskell чистым или считаете монаду IO нечистым, стиль Haskell - это крайняя форма этого стиля, которую стоит изучить.
Монада Haskell IO получена из математической теории (конечно) монад. Тем не менее, для императивных программистов, я думаю, обратный способ достижения монад имеет больше смысла.
Первый этап - чистый функциональный язык может легко вернуть большое строковое значение в качестве результата. Эта большая строка может быть исходным кодом императивной программы, полученной чисто функциональным способом из некоторых параметров, определяющих требования. Затем вы можете создать компилятор «более высокого уровня», который запускает ваш генератор кода, а затем автоматически передает этот сгенерированный код в обязательный языковой компилятор.
Фаза вторая - вместо генерации текстового исходного кода вы генерируете строго типизированное абстрактное синтаксическое дерево. Ваш компилятор императивного языка включается в ваш компилятор "более высокого уровня" и принимает AST непосредственно в качестве исходного кода. Это намного ближе к тому, что делает Haskell.
Это все еще неловко, хотя. Например, у вас есть два разных вида функций - те, которые оцениваются на этапе генерации кода, и те, которые выполняются при запуске сгенерированной программы. Это немного похоже на различие между функциями и шаблонами в C ++.
Таким образом, для фазы 3, сделайте два одинаковых - одна и та же функция с одинаковым синтаксисом может быть частично оценена во время «генерации кода», или полностью оценена, или вообще не оценена. Далее, отбросьте все циклические узлы конструкции AST в пользу рекурсии. Фактически, полностью отбросьте идею об узлах AST как об особом виде данных - не имейте узлов AST с «буквальным значением», просто имейте значения и т. Д.
Это в значительной степени то, что делает монада IO - оператор связывания - это способ составления «действий» для формирования программ. Ничего особенного - просто функция. Многие выражения и функции могут быть оценены во время «генерации кода», но те, которые зависят от побочных эффектов ввода-вывода, должны иметь задержку оценки до времени выполнения - не по какому-либо специальному правилу, а как естественное следствие зависимостей данных в выражения.
Монады в целом являются просто обобщением - они имеют один и тот же интерфейс, но реализуют абстрактные операции по-разному, поэтому вместо оценки в виде императивного кода они вместо этого оценивают что-то другое. Наличие одного и того же интерфейса означает, что есть несколько вещей, которые вы можете делать с монадами, не заботясь о том, какая из них является полезной.
Это описание, несомненно, заставит взорваться головы пуристов, но для меня оно объясняет некоторые из реальных причин, почему Хаскель интересен. Он стирает границу между программированием и метапрограммированием и использует инструменты функционального программирования, чтобы заново изобретать императивное программирование без необходимости специального синтаксиса.
У меня есть критика шаблонов C ++ в том, что они являются своего рода сломанным чистым функциональным подъязыком в императивном языке - чтобы оценить ту же базовую функцию во время компиляции, а не во время выполнения, вам нужно заново реализовать ее, используя совершенно другой стиль кодирования. В Haskell, хотя примеси должны быть помечены как таковые в своем типе, одна и та же функция может быть оценена как в смысле метапрограммирования, так и в смысле неметапрограммирования во время выполнения в одной и той же программе - жесткой линии не существует между программированием и метапрограммированием.
Тем не менее, есть некоторые вещи метапрограммирования, которые не может сделать стандартный Haskell, в основном потому, что типы (и, возможно, некоторые другие вещи) не являются первоклассными значениями. Однако существуют языковые варианты, которые пытаются решить эту проблему.
Многое из того, что я сказал о Haskell, может быть применено на нечистых функциональных языках, а иногда даже на императивных языках. Haskell отличается тем, что у вас нет выбора, кроме как использовать этот подход - он в основном заставляет вас учиться этому стилю работы. Вы можете «написать C на ML», но вы не можете «написать C на Haskell» - по крайней мере, без изучения того, что происходит под капотом.