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]