Как быть c иммутабельными объектами в асинхронной (или многопоточной среде)?

Изучаю ФП, некоторые моменты не понятны.
Вообще программирую на NodeJS и React, однако интересно ФП в более широком понимании чем только в языке JS, поэтому спрашиваю не только по асинхнхронность но и про многопоточность.
Что я понял:
1. Иммутабельность позволяет безопасно работать в асинхронной/многопоточной среде, т.к. у всех всегда полностью инициализированный объект, и он не изменится в процессе работы
2. Легко отслеживать изменения

Собственно вопросы:
Пример: два разный клиента делают запрос на изменение одного и того же объект одновременно. Предположим что для изменения ресурса нужно выполнить некоторые асинхронные операции. Обработчики каждого запроса берут имметабельный объект и каждый изменяет его по своему, так как внутри есть асинхронные операции то они его могу обрабатывать, по сути, паралельно.
3. Не будет ли конфликта по завершению обработки?
4. Или же потребуется перед выполнение асинхронной операции сохранить промежутоное значение обратно в тот стор, откуда объект был взят. В таком случае этот объект может содержать не консистентные данные.
5. Организовывать очередь и производить изменения объектов в сторе только посредством отправки действий в очередь и уже из очереди последовательно выполнять операции?

Может я что-то не понимаю? По ФП нашел только то что иммутабельность, функции высшего порядка и чистые функции. Но как с работать со всем этим не понятно. Пока все синхронно - все красиво, то тогда и ФП особенно и не нужно.
Может что почитать?
  • Вопрос задан
  • 94 просмотра
Пригласить эксперта
Ответы на вопрос 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Функциональное программирование
Седой и строгий
Судя по тексту вопроса, вы мыслите категориями императивного стиля и объектно-ориентированной парадигмы, где переменная - это коробка, в которой лежат данные, а код - это последовательность конкретных действий, типа "возьми данные из коробки, прибавь к ним единицу, положи обратно в коробку". В ФП нет состояния/коробки. В ФП данные трактуются как последовательность их преобразований.

Например есть некоторая функция складывающая аргументы:
function addition(a, b) {
    return a + b;
}

И есть некое значение-аккумулятор X. Значение X не равно 7, значение X равно

addition(addition(addition(addition(addition(addition(1, 1), 1), 1), 1), 1), 1)

или addition(addition(addition(1, 2), 3), 1) или сотням других вариантов. То есть функции от предыдущих состояний, в математическом смысле.

Естественно, в реальном мире не всё так радужно, как в теории. Совсем без состояния обойтись нельзя, и на низком уровне оно всё равно хранится как некоторое значение области памяти. Но функциональщику не приходится заморачиваться мелочами вроде порядка выполнения, синхронизацией и т.п. Он просто описывает высокоуровневые правила преобразований, а обо всём остальном беспокоится среда выполнения.

Например в Clojure один из способов решить задачу подобную описанной вами - использовать атом.
;; Создаём счётчик с нулевым начальным значением
(def counter (atom 0))

;; Применяем к нему функцию инкремента
(swap! counter inc)

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

Если развить задачу до работы с персистентными данными в базе, то для clojure-программиста сложнее она не станет. Достаточно использовать СУБД Datomic, построенную на идее event sourcing'а, при которой в БД хранятся не сами значения, а последовательность операций над ними. Эффект тот же, что и с атомом - сто клиентов одновременно зафиксировали в БД намерение увеличить counter, а сто первый получил результат применения ста инкрементов к начальному значению.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы