Wednesday, 14 January 2009
Using Agents
I've had a hard time groking Clojure agents, so this is mostly just a series of daft micro-examples to understand them together with restating the bleeding obvious.
An agent is created with
Agents are reactive - they'll only do something if you tell them so. All communication with agents is through functions. There are two commands for sending functions to agents,
Without the invocation of
What if the function you are sending has errors? Let's look at a divide by zero:
Errors in agents can be inspected with
So agents seem incredibly simple - why are they so powerful?
An agent is created with
agent
and the associated data. You can use deref
or @
to access the data associated with an agent (same syntax as I mentioned previously for atoms).
user> (agent '(1 2 3 4 5 6))
#
user> @(agent '(1 2 3 4 5 6))
(1 2 3 4 5 6)
Agents are reactive - they'll only do something if you tell them so. All communication with agents is through functions. There are two commands for sending functions to agents,
send
(used for non-blocking calls) and send-off
(used for potentially blocking calls). Both send
and send-off
return immediately, the difference being that send-off
guarantees the message will be processed in a different thread.
user> (let [a (agent 4)]
(send a + 1) ; schedules a call of (apply + agent_state 1)
(await a)
(prn @a))
5
nil
Without the invocation of
await
, this may return 4 not 5. await
blocks the current thread indefinitely until all actions have been completed (which makes it quite dangerous!).What if the function you are sending has errors? Let's look at a divide by zero:
user> (let [a (agent 4)]
(send a / 0)
(await a)
(prn @a))
; Evaluation aborted.
java.lang.Exception: Agent has errors (NO_SOURCE_FILE:0)
[Thrown class clojure.lang.Compiler$CompilerException]
Errors in agents can be inspected with
agent-errors
which returns a sequence of exceptions. Once an agent is in an error state it can not process any more messages until the errors have been cleared with clear-agent-errors
.
user> (let [a (agent 4)]
(send a / 0)
(await a)
(prn (agent-errors a))
(clear-agent-errors a)
(prn @a))
(#)
4
nil
So agents seem incredibly simple - why are they so powerful?
- Integration with STM, which leads too...
- Always observable - no matter what calculation is going on, you can always get the current (valid) value with
deref
. - No message-loops - agents are purely reactive which (to me) seems a simpler programming model to understand
- Open - agents are mutated by functions and therefore this can be extended by anyone (in contrast with having to add a new type of message for the loop to switch on)
Labels: clojure
Subscribe to Posts [Atom]