29. července 2011

Jak měnit neměnitelné. Refs

Clojure používá neměnitelná (immutable) data/objekty. Pokud potřebujeme měnitelná (mutable) data, řeší to Clojure "měnitelnou referencí na neměnitelný objekt" :-) Jedním z prostředků, které toto řeší jsou Refs: jsou to transakční reference, které umožňují bezpečné sdílení měnitelných úložišť pomocí systému STM (Software Transactional Memory).

Měnitelná reference (ref) se vytvoří funkcí ref, její hodnotu vrací buď funkce deref, nebo makro @:
(def my-ref (ref "immutable data"))
; #'user/my-ref
my-ref
; #<ref@18352d8: "immutable data">
@my-ref
; "immutable data"
(deref my-ref)
; "immutable data"
Pokud chceme referenci nastavit na jinou (neměnitelnou) hodnotu, slouží k tomu funkce ref-set:
(ref-set my-ref "another immutable data")
; IllegalStateException No transaction running  clojure.lang.LockingTransaction.getEx (LockingTransaction.java:208)
Jejda! Zapomněli jsme na transakci :-)
(dosync (ref-set my-ref "another immutable data"))
; "another immutable data"
@my-ref
; "another immutable data"
Pokud chceme provést čtení hodnoty a zároveň její změnu v jednom kroku (= aplikovat na hodnotu funkci), je vhodné použít funkci alter:
(dosync (alter my-ref #(apply str (reverse %))))
; "atad elbatummi rehtona"
@my-ref
; "atad elbatummi rehtona"
Na reference je také možné přidat validace:
(def counter (ref 0 :validator number?))
; #'user/counter
(dosync (ref-set counter "string"))
; IllegalStateException Invalid reference state  clojure.lang.ARef.validate (ARef.java:33)
@counter
; 0
(dosync (ref-set counter 42))
; 42
@counter
; 42
O Refs jsem už jednou (trochu) psal. Z dnešního pohledu k tomu mám dvě výhrady:
  • místo ref-set jsem měl použít alter,
  • místo reference jsem měl použít atom.
ref-set je vhodné použít tehdy, pokud přiřazuji novou hodnotu (nepočítám ji). alter tehdy, pokud nad hodnotou provádím nějakou funkci (např. inkrementace, přidání hodnoty do kolekce apod.).

Při rozhodování, jestli použít ref nebo atom je podstatné, jestli využiju transakci - v transakci můžu updatovat více referencí. Pokud budu měnit pouze jedinou hodnotu (bez vazby na cokoli jiného) je vhodnější použít atom (a o těch až někdy příště).

Žádné komentáře:

Okomentovat