Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure differences between Ref, Var, Agent, Atom, with examples

Tags:

var

clojure

agent

I highly recommend "The Joy of Clojure" or "programming Clojure" for a real answer to this question, I can reproduce a short snip-it of the motivations for each:

start by watching this video on the notion of Identity and/or studying here.

  • Refs are for Coordinated Synchronous access to "Many Identities".
  • Atoms are for Uncoordinated synchronous access to a single Identity.
  • Agents are for Uncoordinated asynchronous access to a single Identity.
  • Vars are for thread local isolated identities with a shared default value.

Coordinated access is used when two Identities needs to change together, the classic example is moving money from one bank account to another, it needs to either move completely or not at all.

Uncoordinated access is used when only one Identity needs to update, this is a very common case.

Synchronous access is used when the call is expected to wait until all Identities have settled before continuing.

Asynchronous access is "fire and forget" and let the Identity reach its new state in its own time.


Refs are for state that needs to be synchronized between threads. If you need to keep track of a bunch of different things and you will sometimes need to do operations that write to several of the things at once, use refs. Any time you have multiple different pieces of state, using refs isn't a bad idea.

Atoms are for independent state that needs to be synchronized between threads. If you will never need to change the state of the atom and anything else at the same time, using at atom is safe (in particular, if there is only one piece of state in the entire program, you can put it in an atom). As a non-trivial example, if you are trying to cache the return values of a function (ie memoize it), using an atom is probably safe - the state is invisible to everything outside the function, so you don't need to worry about a state change inside the function messing anything up.

Agents primary point is that they run in a different thread. You can get the value of the agent and tell it to apply a function to its value, but you don't know when the function will run or what value the function will be applied to.

Vars are for when you need to store something on a per-thread basis. If you have a multi-threaded program and each thread needs its own private state, put that state in a var.

As far as real-world examples go, if you provide an example of what you are trying to do, we can tell you what to use.


When I first read about these types, I also struggled to understand where I could or should use each one so here's my plain English answer:

Use a var when the data won't change. This happens whenever you use def or most functions that start with def like defn.

Use an atom when you have a single item that changes. An example might be a counter or a vector that you want to add items to.

Use a ref when you have two or more things that must change at the same time. Think "database transactions" if you are familiar. The canonical example of this is transferring money from one account to another. Each account could be stored in a ref so that changes can be made to appear atomic.

Use an agent when you want something to change but you don't care when. This might be a long computation or writing something to a file or socket. Note that with the latter you should use send-off.

Note: I appreciate that there is quite a lot more to each of these but hopefully this should give you a starting point.


I wrote article with summary up the difference between them and help choose when use which one.

Share state - when use vars, atoms, agents and refs?

I hope it will help people looking answers in that topic.

Some shortcut from the article after @tunaci suggestion:

Vars

Vars are global for every threads.

Do not change vars after create. It is technically possible, but it is bad idea for many reasons.

Atoms

Share access to mutable state for every threads. Change occurs synchronously. Retry when other thread change the state during run.

Do not use not idempotent functions and functions with long time execution

Agents

Share access to mutable state for every threads. Change occurs asynchronously.

Refs

Refs works similar to database transactions. Write and read are protect in dosync. You can operate on many refs safe in transaction.

And flowchart when use which one: flowchart

Please look at the image on website, because some updates are always possible.

It is complex and a long topic to give full answer without copy & past article, so please forgive me i redirect you to website :)