Здравствуйте, Дорогие Хабровчане! Я изучаюHaskell, и для закрепления материала я, используя монадуState, реализовал полноценные переменные! Я человек и могу ошибиться, пожалуйста, поправляйте меня в комментариях. Также будет очень приятно услышать конструктивную критику. Сейчас я выделю несколько важных аспектов: Haskell- статически типизированный, функциональный декларативный язык. Все ф-ии чистые. Это хорошо и удобно. Так весь код - вызов функций, то для вызова функции не требуется скобок:  Названия "переменных" могут содержать штрих: arg'. Все "переменные" - на самом деле константы, и сегодня я буду реализовывать переменные изменяемые. Операторы и функции - одно и тоже, мы можем определить оператор самостоятельно: Также во всевозможных декларациях типов можно встретить в начале что - то типа(Num a, Integral b, Show q, Eq s, ...) => Это значит что в последующей декларации типа типовая переменная реализует какой-либо Тайпкласс (класс типов), в примере, aреализует тайпкласс Num, b- тайпкласс Integral... Класс типов это что - то вроде интерфейса. Но это примерное определение. Парочка операторов, для понимания кода: Я думаю, Вы и так поняли, что я что-то недоговариваю. Все ф-ии в этом замечательном языке по умолчанию каррированны: Также в Haskell есть приятный синтаксис для создания лямбда-функций (т. е. анонимных): Если будут вопросы по синтаксису: добро пожаловать в комментарии и ЛС. Монады - это Тайпкласс (см. прошлый раздел) для оборачиваемых типов, вот его определение:  Для Монад также была придумана do-нотация, "склеивающая" всё что в ней есть с помощью >> Незнающий человек может удивиться: как так, состояние (!) в чистом языке! Однако, хитрые хаскелисты с лёгкостью сделали "невозможное". Для начала,Stateимеет следующее определение: Значит, к примеру, State Integer ()  есть функция, преобразовывающая Integer в кортедж ((), Integer). Подсказываю:s - состояние, а a - значение. "Но, это не монада!", возразите Вы, и будете правы, вот реализация Состояния как Монады: return x, как положено, помещает значение в минимальный контекст, т. е. в данном случае мы заменяем текущее значение a(результат вычислений), а состояние оставляем прежним. В случае привязки немного сложней: используя pattern matching, производится извлечение функции из состояния (h), и далее оборачивается в состояние анонимная функция, принимающая состояние s и возврающая g newState, где newState - состояние, полученное вызовом h s, а g - функция, полученная "разворачиванием" вызова f a. Получается, мы заменяем текущее состояние (т. е. функцию) новым, "наращивая" слои. Также здесь я показал обычную реализацию >>.  State- конструктор типа State, однако по умолчанию он не экспортируется, и для публичного использования предлагается ф-ия state, имитирующая конструктор В функцияхset_ get_ -state мы оборачиваем анонимную функцию, принимающую текущее состояние, в state, а возвращает новое состояние в связке с новым значением. Далее мы используем эти функции в main' используя do-нотацию. Также я показал что находится "под капотом" этого синтаксического сахара. Далее пошагово раскроемы вызовы bind оператора: Вот мы выполнили "грязную работу" компилятора. Надеюсь так станет яснее. Теперь, разобравшись с магией чистого состояния, двигаемся дальше, к переменным. Я собираюсь хранить список переменных как состояние, а сама переменая есть обёртка вокруг кортеджа из имени и значения. К сожалению, таким образом у нас все переменные будут одного типа, однако это не столь проблематично. Здесь определяется новый тип - обёртка Var, И вспомогательные псевдонимы типов Vars (список переменных), VarState(на замену State) и VarEnv(как надстройка VarState, о нём попозже). Далее я определяюVarкак экземпляр тайпклассов Showи Eq: Тайпкласс Show- обьекты отображаемые в строку, Тайпкласс Eq- обьекты которые можно сравнивать (==, /=). Далее, я определяю вспомогательные функции для Var: Функция varнужна чтобы не писать скобки кортеджа как Var (name, value). Далее самое интересное: работаем с переменными. Что мы можем сделать с переменными? Изменить (в т. ч. добавить), получить, удалить. Для реализации можно просто рекурсивно проходиться по списку переменных и делать сопутствующее действие, к примеру, перезаписывать: Получение значения переменной: А теперь удаление переменной. Не отрицаю, чуть - чуть кривое: Ох, вроде всё. Теперь посмотрим на всё это великолепие в коде: Также, я обещал рассказать про используемый здесь VarEnv val. Т. к. в большинстве случаев имя - строка, а результат -Maybeтипа переменной, то для упрощения я создал этот псевдоним типа.   Поздравляю, мы сделали это! Мне было очень интересно работать над этим проектом, и теперь я предлагаю вам, Моим Читателям, для тренировки, реализовать переменные по памяти. Спасибо за прочтение, я очень признателен Вам.