From d4d705e755884728ce868ba1a9fbb5ec7b673d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bon=C3=A9r?= Date: Wed, 31 Mar 2010 20:27:41 +0200 Subject: [PATCH] updated Agent scaladoc with monadic examples --- akka-core/src/main/scala/actor/Agent.scala | 83 +++++++++++++++------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/akka-core/src/main/scala/actor/Agent.scala b/akka-core/src/main/scala/actor/Agent.scala index d202a0d2be..aa90e0135c 100644 --- a/akka-core/src/main/scala/actor/Agent.scala +++ b/akka-core/src/main/scala/actor/Agent.scala @@ -22,23 +22,37 @@ import java.util.concurrent.CountDownLatch class AgentException private[akka](message: String) extends RuntimeException(message) /** -* The Agent class was strongly inspired by the agent principle in Clojure. -* Essentially, an agent wraps a shared mutable state and hides it behind -* a message-passing interface. Agents accept messages and process them on -* behalf of the wrapped state. -* -* Typically agents accept functions / commands as messages and ensure the -* submitted commands are executed against the internal agent's state in a -* thread-safe manner (sequentially). -* -* The submitted functions / commands take the internal state as a parameter -* and their output becomes the new internal state value. -* -* The code that is submitted to an agent doesn't need to pay attention to -* threading or synchronization, the agent will provide such guarantees by itself. +* The Agent class was strongly inspired by the agent principle in Clojure. +*

* -* If an Agent is used within an enclosing transaction, then it will participate -* in that transaction. +* Agents provide independent, asynchronous change of individual locations. +* Agents are bound to a single storage location for their lifetime, and +* only allow mutation of that location (to a new state) to occur as a +* result of an action. Actions are functions (with, optionally, additional +* arguments) that are asynchronously applied to an Agent's state and whose +* return value becomes the Agent's new state. Because the set of functions +* is open, the set of actions supported by an Agent is also open, a sharp +* contrast to pattern matching message handling loops provided by Actors. +*

+* +* Agents are reactive, not autonomous - there is no imperative message loop +* and no blocking receive. The state of an Agent should be itself immutable +* (preferably an instance of one of Akka's persistent collections), and the +* state of an Agent is always immediately available for reading by any +* thread (using the '()' function) without any messages, i.e. observation +* does not require cooperation or coordination. +*

+* +* The actions of all Agents get interleaved amongst threads in a thread pool. +* At any point in time, at most one action for each Agent is being executed. +* Actions dispatched to an agent from another single agent or thread will +* occur in the order they were sent, potentially interleaved with actions +* dispatched to the same agent from other sources. +*

+* +* If an Agent is used within an enclosing transaction, then it will +* participate in that transaction. +*

* * Example of usage: *

@@ -52,25 +66,44 @@ class AgentException private[akka](message: String) extends RuntimeException(mes
 *
 * agent.close
 * 
+*

+* +* Agent is also monadic, which means that you can compose operations using +* for-comprehensions. In monadic usage the original agents are not touched +* but new agents are created. So the old values (agents) are still available +* as-is. They are so-called 'persistent'. +*

* * Example of monadic usage: *

 * val agent1 = Agent(3)
 * val agent2 = Agent(5)
 *
-* for {
-*   first <- agent1
-*   second <- agent2
-*   if first == second
-* } process(first, second)
+* for (value <- agent1) {
+*   result = value + 1 
+* }
+* 
+* val agent3 = 
+*   for (value <- agent1) yield value + 1 
+* 
+* val agent4 = for {
+*   value1 <- agent1
+*   value2 <- agent2
+* } yield value1 + value2 
 *
 * agent1.close
 * agent2.close
+* agent3.close
+* agent4.close
 * 
+*

* -* NOTE: You can't call 'agent.get' or 'agent()' within an enclosing transaction since -* that will block the transaction indefinitely. But 'agent.send' or 'Agent(value)' -* is fine. +* IMPORTANT: +* You can *not* call 'agent.get', 'agent()' or use the monadic 'foreach', +* 'map and 'flatMap' within an enclosing transaction since that would block +* the transaction indefinitely. But all other operations are fine. The system +* will raise an error (e.g. *not* deadlock) if you try to do so, so as long as +* you test your application thoroughly you should be fine. * * @author Viktor Klang * @author Jonas Bonér @@ -117,7 +150,7 @@ sealed class Agent[T] private (initialValue: T) extends Transactor { "Can't call Agent.get within an enclosing transaction.\n\tWould block indefinitely.\n\tPlease refactor your code.") val ref = new AtomicReference[T] val latch = new CountDownLatch(1) - sendProc((x: T) => {ref.set(x); latch.countDown}) + sendProc((v: T) => {ref.set(v); latch.countDown}) latch.await ref.get }