Clojure: Estados #2

Posted on September 4, 2010

0


A função hello criada anteriormente é pura. Isto significa que ela não tem nenhum efeito colateral. Esse tipo de função é relativamente fácil de escrever, testar e entender além de ser
recomendada pra diversas necessidades.

Porém muitos programas possuem estado compartilhado e precisam de funções não puras como essa para manter esse estado compartilhado.

Melhorando a função hello

Nossa função agora manterá um registro de todas as chamadas que forem feitas a ela. Para isso, usaremos um “set” do Clojure.

#{}

#{}

Como adicionar elementos nesse set ? Para isso utilizaremos um “conj“.
Conj é a significa conjoin de forma simplificada e ele cria novas coleções com os ítens adicionados.
Podemos então usar o conj com um elemento em um set.


(conj #{} "Anderson")

#{"Anderson"}


Agora que sabemos como criar “sets“, precisamos de alguma forma manter os registros das chamadas a nossa função hello.
Para isso, Clojure possui referencias (refs) e para nomear uma referência usamos def.

def é quase igual ao defn mas de uma forma mais generalista. Usando def podemos definir funções ou dados.
Usamos def para criar referências e associá-las a nossa função:


(def visitors (ref #{}))

#'user/visitors

E onde está esse controle de estados tão forte em Closure ?

O que acontece se tentarmos alterar essa referência que criamos ?
Vamos tentar fazer isso usando “alter“. Usando “alter” podemos tentar alterar nossa referência, inclusive passando argumentos se necessário. Vamos tentar alterar “visitor” para “visitors”.

(alter visitors conj "Anderson")

java.lang.IllegalStateException: No transaction running

Como vemos acima, clojure protege as referências. Podemos alterá-las mas precisamos de “transactions“, deixando para o Clojure o trabalho pesado de lidar com concorrêcia entre múltiplas chamadas a nossa função.

E como crio essa transaction ?
Para criar uma transaction usamos dosync.

(dosync (alter visitors conj "Anderson"))

#{"Anderson"}

Agora podemos buscar a referência com deref, veja:

(deref visitors)

#{"Anderson"}
@visitors

#{"Anderson"}

Agora podemos construir uma função hello muito mais avançada e elaborada:
(defn hello
"Returns a hello message, calling you by username.
Knows if you have been here before."
[username]
(dosync
(let [past-visitor (@visitors username)]
(if past-visitor
(str "Welcome back, " username)
(do
(alter visitors conj username)
(str "Hello, " username))))))

Posted in: clojure