Я бы сказал, что состояние вообще не является запахом кода, если оно хранится маленьким и хорошо контролируемым.
Это означает, что использование монад, таких как State, ST или заказных, или просто наличие структуры данных, содержащей данные состояния, которые вы передаете в нескольких местах, не так уж плохо.(На самом деле, монады - это просто помощь в этом!) Однако, имея состояние, которое идет повсюду (да, это означает, что вы, МО-монада!) - плохой запах.
Ярким примером тому было то, что моя команда работала над нашей записью для ICFP Programming Contest 2009 (код доступен по адресу git: //git.cynic.net/haskell/icfp-contest-2009). Мы закончили с несколькими различными модульных частей к этому:
- VM: виртуальная машина, которая побежала программы моделирования
- Контроллеры: несколько различных наборов подпрограмм, которые считывают выход имитатора и сгенерированных новые управляющие входы
- Решение: создание файла решения на основе выходных данных контроллеров
- Иллюстраторы: несколько различных наборов подпрограмм, которые считывают как входные, так и выходные порты и генерируют какую-то визуализацию или журнал того, что происходило в качестве моделирования прогресс
Каждое из них имеет свое собственное состояние, и все они взаимодействуют различными способами через входные и выходные значения виртуальной машины. У нас было несколько разных контроллеров и визуализаторов, каждый из которых имел свое собственное состояние.
Ключевым моментом здесь было то, что внутренности любого конкретного состояния были ограничены их собственными конкретными модулями, и каждый модуль ничего не знал о существовании состояния для других модулей. Любой конкретный набор кода и данных с состоянием, как правило, составлял всего несколько десятков строк, с несколькими элементами данных в состоянии.
Все это было склеено в одну небольшую функцию, состоящую из дюжины линий, которые не имели доступа к внутренним элементам любого из состояний и которые просто называли правильные вещи в правильном порядке, когда они зацикливались в симуляции, и передал очень ограниченный объем внешней информации каждому модулю (конечно, вместе с предыдущим состоянием модуля).
Когда состояние используется таким ограниченным образом, и система типов препятствует непреднамеренно ее модификации, ее довольно легко обрабатывать. Это одна из красот Haskell, которая позволяет вам сделать это.
В одном ответе говорится: «Не используйте монады». С моей точки зрения, это точно в обратном направлении. Монады - это структура управления, которая, среди прочего, может помочь вам свести к минимуму количество кода, который касается состояния. Если вы посмотрите на монадические синтаксические анализаторы в качестве примера, состояние анализа (т. Е. Анализируемый текст, как далеко он попал к нему, любые накопленные предупреждения и т. Д.) Должны проходить через каждый комбинатор, используемый в синтаксическом анализаторе , Но будет только несколько комбинаторов, которые фактически манипулируют государством напрямую; все остальное использует одну из этих нескольких функций. Это позволяет вам четко видеть и в одном месте весь небольшой код, который может изменить состояние, и более легко понять, как его можно изменить, что снова облегчает работу.
Wtf man, whitespace ... –
Должен ли я писать компилятор для mips или x86 asm? Это было бы намного сложнее. – Cybis
C++, Cybis. Спецификация 1999 года. И мы хотим это к пятнице. –