Керування станом

Ми вже знаємо, що однією з фундаментальних ідей в ClojureScript є незмінність даних. Скалярні значення та колекції в ClojureScript — незмінні. Змінювати можливо лише об'єкти JavaScript, наприклад Date, тому що вони змінні за своєю сутністю.

Незмінність має багато корисних властивостей, але інколи з плином часу нам потрібна можливість змінювати значення. Як цього досягти, якщо ми не можемо змінювати структури даних?

Змінні

Значення змінних можливо змінювати лише у тому просторі імен, де вони були створені, але при цьому, ми не маємо можливості дізнатись, коли це сталось. Неможливість змінювати значення змінних з інших просторів імен дещо обмежує; також, зазвичай, коли ми змінюємо стан, ми зацікавлені у тому, щоб знати, коли це сталось.

Атоми

ClojureScript має тип Atom, що представляє собою об'єкт, який містить значення, що може бути змінене. Крім того, атом дає можливість спостерігати за зміною значення, за допомогою спостерігаючих функцій, які можна додавати та видаляти з атому. Також є можливість додавати валідуючі функції для перевірки значення атому після його зміни.

Якщо б ми моделювали ідентичність, що описує людину з ім'ям Цирі, ми могли б покласти незмінне значення з описом Цирі у атом. Значення із атому можна прочитати за допомогою функції deref або скороченого позначення @:

(def ciri (atom {:name "Cirilla" :lastname "Fiona" :age 20}))
;; #<Atom: {:name "Cirilla", :lastname "Fiona", :age 20}>

(deref ciri)
;; {:name "Cirilla", :lastname "Fiona", :age 20}

@ciri
;; {:name "Cirilla", :lastname "Fiona", :age 20}

Функція swap! використовується для зміни значення атому за допомогою функції. Сьогодні день народження Цирі, тому давайте збільшимо її вік на одиницю:

(swap! ciri update :age inc)
;; {:name "Cirilla", :lastname "Fiona", :age 21}

@ciri
;; {:name "Cirilla", :lastname "Fiona", :age 21}

The reset! functions replaces the value contained in the atom with a new one:

(reset! ciri {:name "Cirilla", :lastname "Fiona", :age 22})
;; {:name "Cirilla", :lastname "Fiona", :age 22}

@ciri
;; {:name "Cirilla", :lastname "Fiona", :age 22}
Спостереження

Ми можемо додавати та видаляти спостерігаючі функції для атому. Ці функції будуть викликані, коли ми змінемо значення атому за допомогою функції swap! або reset!. Спостерігачі додаються за допомогою функції add-watch. Кожен такий спостерігач пов'язаний з ключем (ключ :logger у наступному прикладі), що потім може бути використаний для видалення спостерігача.

(def a (atom))

(add-watch a :logger (fn [key the-atom old-value new-value]
                       (println "Key:" key "Old:" old-value "New:" new-value)))

(reset! a 42)
;; Key: :logger Old: nil New: 42
;; => 42

(swap! a inc)
;; Key: :logger Old: 42 New: 43
;; => 43

(remove-watch a :logger)

Легкі атоми

Легкі атоми (volatile) схожі на атоми — це обʼєкти, що містять змінюване значення. На відміну від звичайних атомів, легкі атоми не надають можливості спостерігати за зміною значення та виконувати валідацію. Натомість легкі атоми швидші та краще підходять на роль змінного контейнера для значень у функціях зі станом, яким не потрібне спостереження та валідація.

API легких атомів схоже на звичайні атоми. З них можна прочитати значення, яке вони містять, та змінити (або замінити) його за допомогою функцій vswap! та vreset!:

(def ciri (volatile! {:name "Cirilla" :lastname "Fiona" :age 20}))
;; #<Volatile: {:name "Cirilla", :lastname "Fiona", :age 20}>

(volatile? ciri)
;; => true

(deref ciri)
;; {:name "Cirilla", :lastname "Fiona", :age 20}

(vswap! ciri update :age inc)
;; {:name "Cirilla", :lastname "Fiona", :age 21}

(vreset! ciri {:name "Cirilla", :lastname "Fiona", :age 22})
;; {:name "Cirilla", :lastname "Fiona", :age 22}

Зверніть увагу, що ім'я конструктора легкого атому має знак оклику в кінці. Легкі атоми створюються за допомогою функції volatile!, а атоми — за допомогою функції atom.

results matching ""

    No results matching ""