Clojure: когда использовать изменяемое состояние

#clojure

#clojure

Вопрос:

Я реализую небольшую «игровую» вещь в Clojure. Пока я передаю объект «статус мира» среди функций. Это очень «функционально», и я могу смоделировать любой момент игры, просто передав системе выдуманное состояние мира.

Поскольку Clojure имеет очень сложную систему управления состоянием (ссылки, атомы …) Я хотел знать, какой более идиоматичный способ программирования Clojure, использовать ли его систему или придерживаться более функционального подхода.

Спасибо.

Редактировать:

Это интересное чтение, которое я только что нашел, более или менее описывающее шаблон, который я использую.

Ответ №1:

Чистые функции уже являются идиоматическим Clojure. Ссылочные типы (ref, atom, agent) предназначены для координации общего состояния.

Пока ничего не используется совместно (вы не обновляете мир в одном потоке во время рендеринга в другом или не координируете нескольких игроков в их собственных потоках), нет причин делиться состоянием и, следовательно, нет причин отказываться от чисто функционального стиля.

Другим исключением может быть оптимизация производительности: но когда производительность может быть повышена за счет изменчивости, вы хотите сохранить мутации как можно более локальными. Здесь появляются переходные процессы или массивы Java. Функции, которые обертывают эти изменяемые оптимизации, обычно все еще являются чистыми функциями.

Ответ №2:

Clojure — это функциональный язык программирования, разработанный для использования преимуществ многоядерных / SMP-процессоров. Вы можете многое извлечь из функционального языка программирования без доступа к общей памяти, и действительно, Erlang делает это замечательно, но он не использует все возможности процессоров.

Где clojure выделяется по сравнению с языками «модели актера», так это когда несколько потоков хотят работать с одними и теми же данными осмысленным и скоординированным образом. Если у вас есть тривиально распараллеливаемая проблема, такая как обработка изображений, вам не нужны эти преимущества, и вы можете просто отправить один фрагмент данных каждому работнику. Когда эти биты данных взаимозависимы, скоординированный общий доступ становится реальным преимуществом.

В контексте игр вы можете использовать это, чтобы иметь много потоков, обновляющих игровой мир, и один поток, показывающий его пользователю. a ref гарантирует, что пользователь всегда видит согласованный игровой мир, в то время как многие потоки его редактируют.без этого вам нужно было бы, чтобы каждый поток отвечал за определение того, когда показывать его пользователю, или иметь только в потоке.

Еще одна причина использовать такую модель для редактирования игрового мира, помимо скорости, заключается в том, чтобы позволить вам разделять процессы, выполняющие разные задачи, на разные потоки. в одном из ранних примеров clojure Рича он показывает симулятор муравьев, где у каждого муравья есть свой собственный поток, который обновляет позицию муравьев на доске,что привело к очень короткому и простому коду.

Ответ №3:

Написание игры в текущем функциональном стиле — это уже правильный путь, если только один поток или контекст записывает эти данные (что и происходит при повторном запуске). Действительно, ключевое преимущество упомянутых вами средств заключается в работе с общим состоянием. Если вы хотите распараллелить свою программу с помощью многопоточности, то использование этих инструментов может быть полезным.

Комментарии:

1. да, программа очень простая и однопоточная: ввод пользователя -> анализ -> печать чего-либо -> ввод пользователя…

2. Даже если у вас несколько потоков, придерживаться функционального стиля по-прежнему лучший способ — вы хотите иметь как можно больше чистых функций и использовать изменяемые ссылки Clojure только там, где они необходимы для координации доступа к общему состоянию игры (например, наличие атома или ссылки, чтобычтобы поток рендеринга мог видеть последнее состояние игрового мира). Все остальное может быть в значительной степени чистым / неизменяемым.