Atom

Un atom est un type englobant une valeur, c'est à dire qu'il est une référence vers cette dernière. Les atom permettent de modifier cette valeur, ce qui est donc utile pour avoir une variable que l'on peut modifier.

De plus, les atom sont thread-safe, via un mecanisme de compare and set. J'expliquerais plus loin ce que cela signifie

Opérations de base

Un atom se définie via le symbole atom, qui prend en paramètre l'état initial de l'atom. Il est ensuite possible d'intéragir avec l'atom via la fonction swap!.

(def my-atom (atom []))

(swap! my-atom conj 10)

(swap! my-atom (fn [old-state] (conj old-state 20)))

Ici, on définit un atom qui sera stocké dans la variable my-atom. On intéragit ensuite avec lui via la fonction swap!, qui permet de mettre à jour la valeur de l'atom.

On remarque que swap! peut prendre comme paramètre l'atom ainsi qu'une fonction et une suite d'arguments (exemple 1). Dans ce cas, la valeur de l'atom sera passée comme premier paramètre de cette fonction. les autres arguments étant passés à la suite. Dans l'exemple 1, cela donne donc (conj atom-value 10). Le résultat sera la nouvelle valeur de l'atom.

Il est aussi possible de passer à swap l'atom et directement une fonction prenant un paramètre (exemple 2). Dans ce cas, la valeur de l'atom sera le paramètre de cette fonction, et le résultat la nouvelle valeur de l'atom.

Une autre opération intéressante est reset!, qui permet d'assigner à un atom une valeur:

(reset! my-atom [100])

Ici, la nouvelle valeur de l'atom sera 100.

Concurrence

Comme mentionné précédemment, un atom est thread-safe en implémentant un mécanisme de compare and set, soit:

  1. La valeur de l'atom est lue
  2. La fonction soumise via swap! ou reset! est appliquée
  3. La nouvelle valeur est mise dans l'atom si celle-ci n'a pas changée depuis sa lecture (modifiée par un autre thread). Dans le cas contraire, les opérations sont réeffectuées. C'est pourquoi il est important d'éviter les side-effects dans une fonction appliquée à un atom, car cette fonction pourrait être éventuellement rejouée.

Précédent - Suivant -

;; define an atom
(def my-atom (atom []))

;; redef the atom to get its value
(println @my-atom "\n")

;; update the atom value
(swap! my-atom conj 10)

(println @my-atom "\n")

;; You can pass a function to swap!
(swap! my-atom (fn [old-state]
                 (conj old-state 20)))

(println @my-atom "\n")

;; swap returns the new value of the atom
(println (swap! my-atom conj 30) "\n")

;; reset! set the atom value to something
(println (reset! my-atom [100]))

powered by mcorbin - Available on Github