Clojure государства в государствах в государствах - PullRequest
14 голосов
/ 14 ноября 2011

Мне бы очень хотелось услышать, какой совет здесь дают гуру Clojure по управлению государством в иерархиях. Я обнаружил, что часто использую {:structures {:like {:this {:with {:many 'levels}} } } }, и если я хочу отслеживать изменения состояния на нескольких уровнях, подбрасывая атомы вокруг значений (atom {:like (atom 'this)} ), я думаю, что это должно быть неправильно. Как правило, лучше использовать только один атом на верхнем уровне и не иметь ни одного в качестве значений на карте?

Ответы [ 3 ]

13 голосов
/ 02 декабря 2011

Не используйте вложенные атомы в структуре данных, если это вообще возможно.

Основная причина в том, что неизменность - ваш друг.Clojure - это функциональный язык, который процветает на неизменных структурах данных.Большинство библиотек предполагают неизменные структуры данных.STM от Clojure предполагает неизменные структуры данных для получения максимально возможного параллелизма.Неизменность дает вам возможность делать последовательные снимки всего состояния в любой момент.Чистые функции, которые работают с неизменяемыми данными, легко разрабатывать и тестировать.

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

Некоторые предлагаемые альтернативные подходы:

  • Поместите всю свою структуру данных в одну ссылку или атом.Это может быть огромная структура данных без проблем - однажды я написал игру, в которой вся игровая карта без каких-либо затруднений удерживалась в одном атоме.
  • Используйте различные методы, предназначенные для доступа и изменения вложенного неизменяемого объекта.структуры данных: привязка , вставка , обновление и т. д.
  • Использование рекурсивных функций для более удобной навигации по вашей структуре данныхуправляемый.Если в одном узле вашей структуры есть подузлы одного и того же «типа», то обычно хороший совет - использовать рекурсивную функцию.
12 голосов
/ 14 ноября 2011

Вы можете использовать функции assoc-in, get-in, update-in и dissoc-in для работы с вложенными структурами.

Они очень удобны, но я не знаю, смогут ли они справитьсяатомы и такие напрямую.В худшем случае вы должны иметь возможность вложить их до разыменования, например:

(def m (atom {:like {:this {:nested (atom {:value 5})}}}))

@(get-in @m [:like :this :nested])
; => {:value 5}

(get-in @(get-in @m [:like :this :nested]) [:value])
; => 5

Вы можете использовать ->, чтобы сделать это более читабельным:

(-> @m
    (get-in [:like :this :nested])
    deref
    (get-in [:value]))
; => 5

Относительно вложенных атомов/ refs / агентов и т. д. Я думаю, это зависит от того, чего вы пытаетесь достичь.Разумеется, легче рассуждать о вещах, если только одна из них сверху и изменения синхронизированы.

С другой стороны, если вам не нужна эта синхронизация, вы тратите время на выполнениеэто, и вам будет лучше с вложенными атомами / ссылками / агентами.

Суть в том, что я не думаю, что в любом случае "правильный путь", у них обоих есть свои применения.

8 голосов
/ 14 ноября 2011

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

...