Diferenças de Clojure entre Ref, Var, Agent, Atom, com exemplos

110

Eu sou muito novo em Clojure, vocês podem me dar uma explicação com cenários do mundo real. Quer dizer, onde usar Ref, Var, Agent, Atom. Eu li o livro, mas ainda não conseguia entender os exemplos do mundo real.

Mike
fonte

Respostas:

174

Eu recomendo fortemente "The Joy of Clojure" ou "programming Clojure" para uma resposta real a esta pergunta, posso reproduzir um pequeno recorte das motivações para cada um:

comece assistindo este vídeo sobre a noção de Identidade e / ou estudando aqui .

  • Refs são para acesso síncrono coordenado a "Muitas identidades".
  • Os átomos são para acesso síncrono não coordenado a uma única identidade.
  • Agentes são para acesso assíncrono não coordenado a uma única identidade.
  • Vars são para identidades isoladas locais de thread com um valor padrão compartilhado.

O acesso coordenado é usado quando duas identidades precisam ser alteradas juntas, o exemplo clássico é mover dinheiro de uma conta bancária para outra, ele precisa ser movido completamente ou não.

O acesso não coordenado é usado quando apenas uma identidade precisa ser atualizada, este é um caso muito comum.

O acesso síncrono é usado quando se espera que a chamada espere até que todas as identidades tenham sido estabelecidas antes de continuar.

O acesso assíncrono é "dispare e esqueça" e deixe a Identidade atingir seu novo estado em seu próprio tempo.

Arthur Ulfeldt
fonte
No acesso coordenado, se eu quiser apenas alterar state-a, mas consultar ao state-bfazer isso, ainda preciso de um refcorreto? Então, não está mudando várias coisas, mas se referindo a várias coisas enquanto muda qualquer uma delas?
event_jr
2
Sim, você parece entender corretamente que o estado-a e o estado-b devem ser ambos refs. Se você deseja que o novo valor no estado-a seja baseado em uma combinação consistente dos valores em a e b. Você precisa que esse novo valor tenha sido calculado em um contexto onde o estado-a e o estado-b fossem consistentes um com o outro. Quando ambos são refs, se b mudar no meio do caminho, a transação será reiniciada e usará os novos valores de a e b. considere usar a ensurefunção: clojure.github.io/clojure/clojure.core-api.html#clojure.core/… para tornar isso explícito e mais eficiente.
Arthur Ulfeldt,
3
Talvez uma explicação sobre o que significa Isolado com padrão compartilhado pudesse ser adicionada para completar a resposta?
Didier A.
1
"O acesso coordenado é usado quando duas identidades precisam ser alteradas juntas ...". Isso deveria ser "mudado"?
Carcigenicar
40

Refs são para estados que precisam ser sincronizados entre os threads. Se você precisa acompanhar um monte de coisas diferentes e às vezes precisa fazer operações que gravam em várias coisas de uma vez, use refs. Sempre que você tiver vários estados diferentes, usar referências não é uma má ideia.

Os átomos são para estados independentes que precisam ser sincronizados entre os threads. Se você nunca precisará mudar o estado do átomo e qualquer outra coisa ao mesmo tempo, usar at atom é seguro (em particular, se houver apenas um pedaço de estado em todo o programa, você pode colocá-lo em um átomo) . Como um exemplo não trivial, se você está tentando armazenar em cache os valores de retorno de uma função (ou seja, memorizá-la), usar um átomo provavelmente é seguro - o estado é invisível para tudo fora da função, então você não precisa se preocupar sobre uma mudança de estado dentro da função bagunçando alguma coisa.

O ponto principal dos agentes é que eles são executados em um segmento diferente. Você pode obter o valor do agente e dizer a ele para aplicar uma função a seu valor, mas não sabe quando a função será executada ou a qual valor a função será aplicada.

Vars são para quando você precisa armazenar algo por thread. Se você tiver um programa multi-threaded e cada thread precisa de seu próprio estado privado, coloque esse estado em uma var.

No que diz respeito aos exemplos do mundo real, se você fornecer um exemplo do que está tentando fazer, podemos dizer o que usar.

Retief
fonte
32

Quando li pela primeira vez sobre esses tipos, também tive dificuldade para entender onde poderia ou deveria usar cada um, então aqui está minha resposta simples em inglês:

Use uma var quando os dados não mudarem. Isso acontece sempre que você usa defou a maioria das funções que começam com deflike defn.

Use um átomo quando você tiver um único item que muda. Um exemplo pode ser um contador ou vetor ao qual você deseja adicionar itens.

Use um ref quando você tiver duas ou mais coisas que devem mudar ao mesmo tempo. Pense em "transações de banco de dados" se você estiver familiarizado. O exemplo canônico disso é a transferência de dinheiro de uma conta para outra. Cada conta pode ser armazenada em um ref para que as alterações possam ser feitas para parecerem atômicas.

Use um agente quando quiser que algo mude, mas não se importa quando. Isso pode ser um cálculo longo ou escrever algo em um arquivo ou socket. Observe que com o último você deve usar send-off.

Nota: Eu entendo que há muito mais em cada um deles, mas espero que isso seja um ponto de partida.

optevo
fonte
1
Muito obrigado pela sua resposta clara :-) Ajuda muito um novato em Clojure como eu.
gosukiwi de
27

Escrevi artigo com resumo da diferença entre eles e ajudo a escolher quando usar qual deles.

Compartilhar estado - quando usar vars, atoms, agents e refs?

Espero que ajude as pessoas que procuram respostas nesse tópico.

Alguns atalhos do artigo após a sugestão de @tunaci:

Vars

Vars são globais para todos os tópicos.

Não mude vars após criar. É tecnicamente possível, mas é uma má ideia por vários motivos.

Átomos

Compartilhe o acesso ao estado mutável para todos os threads. A mudança ocorre de forma síncrona. Tente novamente quando outro encadeamento mudar de estado durante a execução.

Não use funções não idempotentes e funções com longa execução

Agentes

Compartilhe o acesso ao estado mutável para todos os threads. A mudança ocorre de forma assíncrona.

Refs

Refs funciona de forma semelhante às transações do banco de dados. A gravação e a leitura são protegidas no dosync. Você pode operar em muitos refs com segurança na transação.

E fluxograma quando usar qual: fluxograma

Veja a imagem no site, pois algumas atualizações sempre são possíveis.

É complexo e um tópico longo para dar uma resposta completa sem uma cópia do artigo anterior, então, por favor, me perdoe, eu redireciono você para o site :)

kabra
fonte